From 379bdd1e05db11e907c69bd9a8ddd133fa54a7df Mon Sep 17 00:00:00 2001 From: TsFreddie Date: Fri, 6 Dec 2024 13:14:05 +0800 Subject: [PATCH 01/40] support escaped double quote --- scripts/languages/twlang.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/languages/twlang.py b/scripts/languages/twlang.py index 28b3fe1d5ac..a5758435974 100644 --- a/scripts/languages/twlang.py +++ b/scripts/languages/twlang.py @@ -54,7 +54,7 @@ def decode(fileobj, elements_per_key): def check_file(path): with open(path, encoding="utf-8") as fileobj: - matches = re.findall(r"(Localize|Localizable)\s*\(\s*\"([^\"]+)\"(?:\s*,\s*\"([^\"]+)\")?\s*\)", fileobj.read()) + matches = re.findall(r"(Localize|Localizable)\s*\(\s*\"((?:(?:\\\")|[^\"])+)\"(?:\s*,\s*\"((?:(?:\\\")|[^\"])+)\")?\s*\)", fileobj.read()) return matches @@ -66,7 +66,8 @@ def check_folder(path): if not any(f.endswith(x) for x in [".cpp", ".c", ".h"]): continue for sentence in check_file(os.path.join(path2, f)): - englishlist[sentence[1:]] = None + key = (sentence[1:][0].replace("\\\"", "\""), sentence[1:][1].replace("\\\"", "\"")) + englishlist[key] = None return englishlist From 2174cd003fda604a10606f00a9f32e151afd5580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 8 Dec 2024 12:21:08 +0100 Subject: [PATCH 02/40] Combine `CCollision::IsMover`/`CpSpeed` functions as `MoverSpeed` Avoid duplicate code and improve readability by combining the `CCollision::IsMover` and `CCollision::CpSpeed` functions as the `CCollision::MoverSpeed` function. --- .../client/prediction/entities/dragger.cpp | 7 +- .../client/prediction/entities/pickup.cpp | 5 +- src/game/collision.cpp | 71 +++++++++---------- src/game/collision.h | 4 +- src/game/server/entities/dragger.cpp | 7 +- src/game/server/entities/gun.cpp | 7 +- src/game/server/entities/light.cpp | 8 +-- src/game/server/entities/pickup.cpp | 7 +- 8 files changed, 40 insertions(+), 76 deletions(-) diff --git a/src/game/client/prediction/entities/dragger.cpp b/src/game/client/prediction/entities/dragger.cpp index 2daba1ee20b..24c35f290e5 100644 --- a/src/game/client/prediction/entities/dragger.cpp +++ b/src/game/client/prediction/entities/dragger.cpp @@ -13,12 +13,7 @@ void CDragger::Tick() { if(GameWorld()->GameTick() % (int)(GameWorld()->GameTickSpeed() * 0.15f) == 0) { - int Flags; - int index = Collision()->IsMover(m_Pos.x, m_Pos.y, &Flags); - if(index) - { - m_Core = Collision()->CpSpeed(index, Flags); - } + Collision()->MoverSpeed(m_Pos.x, m_Pos.y, &m_Core); m_Pos += m_Core; LookForPlayersToDrag(); diff --git a/src/game/client/prediction/entities/pickup.cpp b/src/game/client/prediction/entities/pickup.cpp index ff25b70081c..f856be6318e 100644 --- a/src/game/client/prediction/entities/pickup.cpp +++ b/src/game/client/prediction/entities/pickup.cpp @@ -133,12 +133,9 @@ void CPickup::Move() { if(GameWorld()->GameTick() % (int)(GameWorld()->GameTickSpeed() * 0.15f) == 0) { - int Flags; - int index = Collision()->IsMover(m_Pos.x, m_Pos.y, &Flags); - if(index) + if(Collision()->MoverSpeed(m_Pos.x, m_Pos.y, &m_Core)) { m_IsCoreActive = true; - m_Core = Collision()->CpSpeed(index, Flags); } m_Pos += m_Core; } diff --git a/src/game/collision.cpp b/src/game/collision.cpp index acdc51558cd..aaac190f6f6 100644 --- a/src/game/collision.cpp +++ b/src/game/collision.cpp @@ -786,51 +786,46 @@ int CCollision::GetSwitchDelay(int Index) const return 0; } -int CCollision::IsMover(int x, int y, int *pFlags) const +int CCollision::MoverSpeed(int x, int y, vec2 *pSpeed) const { int Nx = clamp(x / 32, 0, m_Width - 1); int Ny = clamp(y / 32, 0, m_Height - 1); int Index = m_pTiles[Ny * m_Width + Nx].m_Index; - *pFlags = m_pTiles[Ny * m_Width + Nx].m_Flags; - if(Index < 0) - return 0; - if(Index == TILE_CP || Index == TILE_CP_F) - return Index; - else + + if(Index != TILE_CP && Index != TILE_CP_F) + { return 0; -} + } -vec2 CCollision::CpSpeed(int Index, int Flags) const -{ - if(Index < 0) - return vec2(0, 0); - vec2 target; - if(Index == TILE_CP || Index == TILE_CP_F) - switch(Flags) - { - case ROTATION_0: - target.x = 0; - target.y = -4; - break; - case ROTATION_90: - target.x = 4; - target.y = 0; - break; - case ROTATION_180: - target.x = 0; - target.y = 4; - break; - case ROTATION_270: - target.x = -4; - target.y = 0; - break; - default: - target = vec2(0, 0); - break; - } + vec2 Target; + switch(m_pTiles[Ny * m_Width + Nx].m_Flags) + { + case ROTATION_0: + Target.x = 0.0f; + Target.y = -4.0f; + break; + case ROTATION_90: + Target.x = 4.0f; + Target.y = 0.0f; + break; + case ROTATION_180: + Target.x = 0.0f; + Target.y = 4.0f; + break; + case ROTATION_270: + Target.x = -4.0f; + Target.y = 0.0f; + break; + default: + Target = vec2(0.0f, 0.0f); + break; + } if(Index == TILE_CP_F) - target *= 4; - return target; + { + Target *= 4.0f; + } + *pSpeed = Target; + return Index; } int CCollision::GetPureMapIndex(float x, float y) const diff --git a/src/game/collision.h b/src/game/collision.h index a5f987488b1..b97097bba75 100644 --- a/src/game/collision.h +++ b/src/game/collision.h @@ -110,9 +110,7 @@ class CCollision int IsTimeCheckpoint(int Index) const; int IsFTimeCheckpoint(int Index) const; - int IsMover(int x, int y, int *pFlags) const; - - vec2 CpSpeed(int index, int Flags = 0) const; + int MoverSpeed(int x, int y, vec2 *pSpeed) const; const CLayers *Layers() const { return m_pLayers; } const CTile *GameLayer() const { return m_pTiles; } diff --git a/src/game/server/entities/dragger.cpp b/src/game/server/entities/dragger.cpp index 4b9a96bd90e..0993d01d352 100644 --- a/src/game/server/entities/dragger.cpp +++ b/src/game/server/entities/dragger.cpp @@ -36,13 +36,8 @@ void CDragger::Tick() { if(Server()->Tick() % (int)(Server()->TickSpeed() * 0.15f) == 0) { - int Flags; m_EvalTick = Server()->Tick(); - int index = GameServer()->Collision()->IsMover(m_Pos.x, m_Pos.y, &Flags); - if(index) - { - m_Core = GameServer()->Collision()->CpSpeed(index, Flags); - } + GameServer()->Collision()->MoverSpeed(m_Pos.x, m_Pos.y, &m_Core); m_Pos += m_Core; // Adopt the new position for all outgoing laser beams diff --git a/src/game/server/entities/gun.cpp b/src/game/server/entities/gun.cpp index 993407f45e0..c2cec218e4d 100644 --- a/src/game/server/entities/gun.cpp +++ b/src/game/server/entities/gun.cpp @@ -33,13 +33,8 @@ void CGun::Tick() { if(Server()->Tick() % (int)(Server()->TickSpeed() * 0.15f) == 0) { - int Flags; m_EvalTick = Server()->Tick(); - int index = GameServer()->Collision()->IsMover(m_Pos.x, m_Pos.y, &Flags); - if(index) - { - m_Core = GameServer()->Collision()->CpSpeed(index, Flags); - } + GameServer()->Collision()->MoverSpeed(m_Pos.x, m_Pos.y, &m_Core); m_Pos += m_Core; } if(g_Config.m_SvPlasmaPerSec > 0) diff --git a/src/game/server/entities/light.cpp b/src/game/server/entities/light.cpp index bc862caa0fd..bdeb32c4b51 100644 --- a/src/game/server/entities/light.cpp +++ b/src/game/server/entities/light.cpp @@ -86,14 +86,8 @@ void CLight::Tick() { if(Server()->Tick() % (int)(Server()->TickSpeed() * 0.15f) == 0) { - int Flags; m_EvalTick = Server()->Tick(); - int index = GameServer()->Collision()->IsMover(m_Pos.x, m_Pos.y, - &Flags); - if(index) - { - m_Core = GameServer()->Collision()->CpSpeed(index, Flags); - } + GameServer()->Collision()->MoverSpeed(m_Pos.x, m_Pos.y, &m_Core); m_Pos += m_Core; Step(); } diff --git a/src/game/server/entities/pickup.cpp b/src/game/server/entities/pickup.cpp index a622c02bef5..a0811cd74ec 100644 --- a/src/game/server/entities/pickup.cpp +++ b/src/game/server/entities/pickup.cpp @@ -190,12 +190,7 @@ void CPickup::Move() { if(Server()->Tick() % (int)(Server()->TickSpeed() * 0.15f) == 0) { - int Flags; - int index = GameServer()->Collision()->IsMover(m_Pos.x, m_Pos.y, &Flags); - if(index) - { - m_Core = GameServer()->Collision()->CpSpeed(index, Flags); - } + GameServer()->Collision()->MoverSpeed(m_Pos.x, m_Pos.y, &m_Core); m_Pos += m_Core; } } From 366a8b1f244505a837ba38250af6c0a95b27f370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 8 Dec 2024 12:41:56 +0100 Subject: [PATCH 03/40] Combine `CCollision::GetDTile*` functions as `GetDoorTile` Simplify usage by combining the `CCollision::GetDTileIndex`, `CCollision::GetDTileFlags` and `CCollision::GetDTileNumber` functions as the `CCollision::GetDoorTile` function. --- src/game/collision.cpp | 36 ++++++++++++------------------------ src/game/collision.h | 4 +--- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/src/game/collision.cpp b/src/game/collision.cpp index aaac190f6f6..dcc7beac352 100644 --- a/src/game/collision.cpp +++ b/src/game/collision.cpp @@ -300,12 +300,11 @@ int CCollision::GetMoveRestrictions(CALLBACK_SWITCHACTIVE pfnSwitchActive, void } if(pfnSwitchActive) { - int TeleNumber = GetDTileNumber(ModMapIndex); - if(pfnSwitchActive(TeleNumber, pUser)) + CDoorTile DoorTile; + GetDoorTile(ModMapIndex, &DoorTile); + if(pfnSwitchActive(DoorTile.m_Number, pUser)) { - int Tile = GetDTileIndex(ModMapIndex); - int Flags = GetDTileFlags(ModMapIndex); - Restrictions |= ::GetMoveRestrictions(d, Tile, Flags); + Restrictions |= ::GetMoveRestrictions(d, DoorTile.m_Index, DoorTile.m_Flags); } } } @@ -1093,27 +1092,16 @@ void CCollision::SetDCollisionAt(float x, float y, int Type, int Flags, int Numb m_pDoor[Ny * m_Width + Nx].m_Number = Number; } -int CCollision::GetDTileIndex(int Index) const +void CCollision::GetDoorTile(int Index, CDoorTile *pDoorTile) const { if(!m_pDoor || Index < 0 || !m_pDoor[Index].m_Index) - return 0; - return m_pDoor[Index].m_Index; -} - -int CCollision::GetDTileNumber(int Index) const -{ - if(!m_pDoor || Index < 0 || !m_pDoor[Index].m_Index) - return 0; - if(m_pDoor[Index].m_Number) - return m_pDoor[Index].m_Number; - return 0; -} - -int CCollision::GetDTileFlags(int Index) const -{ - if(!m_pDoor || Index < 0 || !m_pDoor[Index].m_Index) - return 0; - return m_pDoor[Index].m_Flags; + { + pDoorTile->m_Index = 0; + pDoorTile->m_Flags = 0; + pDoorTile->m_Number = 0; + return; + } + *pDoorTile = m_pDoor[Index]; } void ThroughOffset(vec2 Pos0, vec2 Pos1, int *pOffsetX, int *pOffsetY) diff --git a/src/game/collision.h b/src/game/collision.h index b97097bba75..17913275196 100644 --- a/src/game/collision.h +++ b/src/game/collision.h @@ -55,9 +55,7 @@ class CCollision // DDRace void SetCollisionAt(float x, float y, int Index); void SetDCollisionAt(float x, float y, int Type, int Flags, int Number); - int GetDTileIndex(int Index) const; - int GetDTileFlags(int Index) const; - int GetDTileNumber(int Index) const; + void GetDoorTile(int Index, CDoorTile *pDoorTile) const; int GetFCollisionAt(float x, float y) const { return GetFTile(round_to_int(x), round_to_int(y)); } int IntersectNoLaser(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const; int IntersectNoLaserNW(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const; From 36568ec7e16ec21c7094bf0925b561bf57b7d1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 8 Dec 2024 12:46:03 +0100 Subject: [PATCH 04/40] Rename `CCollision` functions for readability: avoid abbreviations - `SetDCollisionAt` --> `SetDoorCollisionAt` - `GetFCollisionAt` --> `GetFrontCollisionAt` - `IntersectNoLaserNW` --> `IntersectNoLaserNoWalls` - `GetFIndex` --> `GetFrontIndex` - `GetFTile` --> `GetFrontTile` - `GetFTileIndex` --> `GetFrontTileIndex` - `GetFTileFlags` --> `GetFrontTileFlags` - `IsFNoLaser` --> `IsFrontNoLaser` - `IsFTimeCheckpoint` --> `IsFrontTimeCheckpoint` --- .../client/prediction/entities/character.cpp | 4 +- .../client/prediction/entities/dragger.cpp | 4 +- src/game/client/race.cpp | 4 +- src/game/collision.cpp | 38 +++++++++---------- src/game/collision.h | 18 ++++----- src/game/server/entities/character.cpp | 14 +++---- src/game/server/entities/door.cpp | 4 +- src/game/server/entities/dragger.cpp | 2 +- src/game/server/entities/dragger_beam.cpp | 2 +- src/game/server/entities/laser.cpp | 2 +- src/game/server/entities/projectile.cpp | 2 +- src/game/server/gamemodes/DDRace.cpp | 10 ++--- 12 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/game/client/prediction/entities/character.cpp b/src/game/client/prediction/entities/character.cpp index a3369651b29..69d41eddaff 100644 --- a/src/game/client/prediction/entities/character.cpp +++ b/src/game/client/prediction/entities/character.cpp @@ -708,7 +708,7 @@ void CCharacter::HandleTiles(int Index) { int MapIndex = Index; m_TileIndex = Collision()->GetTileIndex(MapIndex); - m_TileFIndex = Collision()->GetFTileIndex(MapIndex); + m_TileFIndex = Collision()->GetFrontTileIndex(MapIndex); m_MoveRestrictions = Collision()->GetMoveRestrictions(IsSwitchActiveCb, this, m_Pos); // stopper @@ -984,7 +984,7 @@ void CCharacter::DDRaceTick() int Index = Collision()->GetPureMapIndex(m_Pos); const int aTiles[] = { Collision()->GetTileIndex(Index), - Collision()->GetFTileIndex(Index), + Collision()->GetFrontTileIndex(Index), Collision()->GetSwitchType(Index)}; m_Core.m_IsInFreeze = false; for(const int Tile : aTiles) diff --git a/src/game/client/prediction/entities/dragger.cpp b/src/game/client/prediction/entities/dragger.cpp index 24c35f290e5..f268d9e6b44 100644 --- a/src/game/client/prediction/entities/dragger.cpp +++ b/src/game/client/prediction/entities/dragger.cpp @@ -57,7 +57,7 @@ void CDragger::LookForPlayersToDrag() // Dragger beams can be created only for reachable, alive players int IsReachable = m_IgnoreWalls ? - !Collision()->IntersectNoLaserNW(m_Pos, pTarget->m_Pos, 0, 0) : + !Collision()->IntersectNoLaserNoWalls(m_Pos, pTarget->m_Pos, 0, 0) : !Collision()->IntersectNoLaser(m_Pos, pTarget->m_Pos, 0, 0); if(IsReachable) { @@ -109,7 +109,7 @@ void CDragger::DraggerBeamTick() // When the dragger can no longer reach the target player, the dragger beam dissolves int IsReachable = m_IgnoreWalls ? - !Collision()->IntersectNoLaserNW(m_Pos, pTarget->m_Pos, 0, 0) : + !Collision()->IntersectNoLaserNoWalls(m_Pos, pTarget->m_Pos, 0, 0) : !Collision()->IntersectNoLaser(m_Pos, pTarget->m_Pos, 0, 0); if(!IsReachable || distance(pTarget->m_Pos, m_Pos) >= g_Config.m_SvDraggerRange) { diff --git a/src/game/client/race.cpp b/src/game/client/race.cpp index 2b5c5f4ef6b..1d86da27c1c 100644 --- a/src/game/client/race.cpp +++ b/src/game/client/race.cpp @@ -109,7 +109,7 @@ bool CRaceHelper::IsStart(vec2 Prev, vec2 Pos) const { if(m_pGameClient->Collision()->GetTileIndex(Index) == TILE_START) return true; - if(m_pGameClient->Collision()->GetFTileIndex(Index) == TILE_START) + if(m_pGameClient->Collision()->GetFrontTileIndex(Index) == TILE_START) return true; } } @@ -118,7 +118,7 @@ bool CRaceHelper::IsStart(vec2 Prev, vec2 Pos) const const int Index = m_pGameClient->Collision()->GetPureMapIndex(Pos); if(m_pGameClient->Collision()->GetTileIndex(Index) == TILE_START) return true; - if(m_pGameClient->Collision()->GetFTileIndex(Index) == TILE_START) + if(m_pGameClient->Collision()->GetFrontTileIndex(Index) == TILE_START) return true; } } diff --git a/src/game/collision.cpp b/src/game/collision.cpp index dcc7beac352..20ee5f188d9 100644 --- a/src/game/collision.cpp +++ b/src/game/collision.cpp @@ -293,8 +293,8 @@ int CCollision::GetMoveRestrictions(CALLBACK_SWITCHACTIVE pfnSwitchActive, void } else { - Tile = GetFTileIndex(ModMapIndex); - Flags = GetFTileFlags(ModMapIndex); + Tile = GetFrontTileIndex(ModMapIndex); + Flags = GetFrontTileFlags(ModMapIndex); } Restrictions |= ::GetMoveRestrictions(d, Tile, Flags); } @@ -640,9 +640,9 @@ int CCollision::IsNoLaser(int x, int y) const return (CCollision::GetTile(x, y) == TILE_NOLASER); } -int CCollision::IsFNoLaser(int x, int y) const +int CCollision::IsFrontNoLaser(int x, int y) const { - return (CCollision::GetFTile(x, y) == TILE_NOLASER); + return (CCollision::GetFrontTile(x, y) == TILE_NOLASER); } int CCollision::IsTeleport(int Index) const @@ -969,7 +969,7 @@ int CCollision::GetTileIndex(int Index) const return m_pTiles[Index].m_Index; } -int CCollision::GetFTileIndex(int Index) const +int CCollision::GetFrontTileIndex(int Index) const { if(Index < 0 || !m_pFront) return 0; @@ -983,7 +983,7 @@ int CCollision::GetTileFlags(int Index) const return m_pTiles[Index].m_Flags; } -int CCollision::GetFTileFlags(int Index) const +int CCollision::GetFrontTileFlags(int Index) const { if(Index < 0 || !m_pFront) return 0; @@ -1027,14 +1027,14 @@ int CCollision::GetIndex(vec2 PrevPos, vec2 Pos) const return -1; } -int CCollision::GetFIndex(int Nx, int Ny) const +int CCollision::GetFrontIndex(int Nx, int Ny) const { if(!m_pFront) return 0; return m_pFront[Ny * m_Width + Nx].m_Index; } -int CCollision::GetFTile(int x, int y) const +int CCollision::GetFrontTile(int x, int y) const { if(!m_pFront) return 0; @@ -1080,7 +1080,7 @@ void CCollision::SetCollisionAt(float x, float y, int Index) m_pTiles[Ny * m_Width + Nx].m_Index = Index; } -void CCollision::SetDCollisionAt(float x, float y, int Type, int Flags, int Number) +void CCollision::SetDoorCollisionAt(float x, float y, int Type, int Flags, int Number) { if(!m_pDoor) return; @@ -1147,14 +1147,14 @@ int CCollision::IntersectNoLaser(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 vec2 Pos = mix(Pos0, Pos1, a); int Nx = clamp(round_to_int(Pos.x) / 32, 0, m_Width - 1); int Ny = clamp(round_to_int(Pos.y) / 32, 0, m_Height - 1); - if(GetIndex(Nx, Ny) == TILE_SOLID || GetIndex(Nx, Ny) == TILE_NOHOOK || GetIndex(Nx, Ny) == TILE_NOLASER || GetFIndex(Nx, Ny) == TILE_NOLASER) + if(GetIndex(Nx, Ny) == TILE_SOLID || GetIndex(Nx, Ny) == TILE_NOHOOK || GetIndex(Nx, Ny) == TILE_NOLASER || GetFrontIndex(Nx, Ny) == TILE_NOLASER) { if(pOutCollision) *pOutCollision = Pos; if(pOutBeforeCollision) *pOutBeforeCollision = Last; - if(GetFIndex(Nx, Ny) == TILE_NOLASER) - return GetFCollisionAt(Pos.x, Pos.y); + if(GetFrontIndex(Nx, Ny) == TILE_NOLASER) + return GetFrontCollisionAt(Pos.x, Pos.y); else return GetCollisionAt(Pos.x, Pos.y); } @@ -1167,7 +1167,7 @@ int CCollision::IntersectNoLaser(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 return 0; } -int CCollision::IntersectNoLaserNW(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const +int CCollision::IntersectNoLaserNoWalls(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const { float d = distance(Pos0, Pos1); vec2 Last = Pos0; @@ -1176,7 +1176,7 @@ int CCollision::IntersectNoLaserNW(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, ve { float a = (float)i / d; vec2 Pos = mix(Pos0, Pos1, a); - if(IsNoLaser(round_to_int(Pos.x), round_to_int(Pos.y)) || IsFNoLaser(round_to_int(Pos.x), round_to_int(Pos.y))) + if(IsNoLaser(round_to_int(Pos.x), round_to_int(Pos.y)) || IsFrontNoLaser(round_to_int(Pos.x), round_to_int(Pos.y))) { if(pOutCollision) *pOutCollision = Pos; @@ -1185,7 +1185,7 @@ int CCollision::IntersectNoLaserNW(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, ve if(IsNoLaser(round_to_int(Pos.x), round_to_int(Pos.y))) return GetCollisionAt(Pos.x, Pos.y); else - return GetFCollisionAt(Pos.x, Pos.y); + return GetFrontCollisionAt(Pos.x, Pos.y); } Last = Pos; } @@ -1205,18 +1205,18 @@ int CCollision::IntersectAir(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pO { float a = (float)i / d; vec2 Pos = mix(Pos0, Pos1, a); - if(IsSolid(round_to_int(Pos.x), round_to_int(Pos.y)) || (!GetTile(round_to_int(Pos.x), round_to_int(Pos.y)) && !GetFTile(round_to_int(Pos.x), round_to_int(Pos.y)))) + if(IsSolid(round_to_int(Pos.x), round_to_int(Pos.y)) || (!GetTile(round_to_int(Pos.x), round_to_int(Pos.y)) && !GetFrontTile(round_to_int(Pos.x), round_to_int(Pos.y)))) { if(pOutCollision) *pOutCollision = Pos; if(pOutBeforeCollision) *pOutBeforeCollision = Last; - if(!GetTile(round_to_int(Pos.x), round_to_int(Pos.y)) && !GetFTile(round_to_int(Pos.x), round_to_int(Pos.y))) + if(!GetTile(round_to_int(Pos.x), round_to_int(Pos.y)) && !GetFrontTile(round_to_int(Pos.x), round_to_int(Pos.y))) return -1; else if(!GetTile(round_to_int(Pos.x), round_to_int(Pos.y))) return GetTile(round_to_int(Pos.x), round_to_int(Pos.y)); else - return GetFTile(round_to_int(Pos.x), round_to_int(Pos.y)); + return GetFrontTile(round_to_int(Pos.x), round_to_int(Pos.y)); } Last = Pos; } @@ -1238,7 +1238,7 @@ int CCollision::IsTimeCheckpoint(int Index) const return -1; } -int CCollision::IsFTimeCheckpoint(int Index) const +int CCollision::IsFrontTimeCheckpoint(int Index) const { if(Index < 0 || !m_pFront) return -1; diff --git a/src/game/collision.h b/src/game/collision.h index 17913275196..fef1b673a8b 100644 --- a/src/game/collision.h +++ b/src/game/collision.h @@ -54,15 +54,15 @@ class CCollision // DDRace void SetCollisionAt(float x, float y, int Index); - void SetDCollisionAt(float x, float y, int Type, int Flags, int Number); + void SetDoorCollisionAt(float x, float y, int Type, int Flags, int Number); void GetDoorTile(int Index, CDoorTile *pDoorTile) const; - int GetFCollisionAt(float x, float y) const { return GetFTile(round_to_int(x), round_to_int(y)); } + int GetFrontCollisionAt(float x, float y) const { return GetFrontTile(round_to_int(x), round_to_int(y)); } int IntersectNoLaser(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const; - int IntersectNoLaserNW(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const; + int IntersectNoLaserNoWalls(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const; int IntersectAir(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const; int GetIndex(int x, int y) const; int GetIndex(vec2 PrevPos, vec2 Pos) const; - int GetFIndex(int x, int y) const; + int GetFrontIndex(int x, int y) const; int GetMoveRestrictions(CALLBACK_SWITCHACTIVE pfnSwitchActive, void *pUser, vec2 Pos, float Distance = 18.0f, int OverrideCenterTileIndex = -1) const; int GetMoveRestrictions(vec2 Pos, float Distance = 18.0f) const @@ -71,7 +71,7 @@ class CCollision } int GetTile(int x, int y) const; - int GetFTile(int x, int y) const; + int GetFrontTile(int x, int y) const; int Entity(int x, int y, int Layer) const; int GetPureMapIndex(float x, float y) const; int GetPureMapIndex(vec2 Pos) const { return GetPureMapIndex(Pos.x, Pos.y); } @@ -81,9 +81,9 @@ class CCollision bool TileExistsNext(int Index) const; vec2 GetPos(int Index) const; int GetTileIndex(int Index) const; - int GetFTileIndex(int Index) const; + int GetFrontTileIndex(int Index) const; int GetTileFlags(int Index) const; - int GetFTileFlags(int Index) const; + int GetFrontTileFlags(int Index) const; int IsTeleport(int Index) const; int IsEvilTeleport(int Index) const; bool IsCheckTeleport(int Index) const; @@ -103,10 +103,10 @@ class CCollision bool IsHookBlocker(int x, int y, vec2 Pos0, vec2 Pos1) const; int IsWallJump(int Index) const; int IsNoLaser(int x, int y) const; - int IsFNoLaser(int x, int y) const; + int IsFrontNoLaser(int x, int y) const; int IsTimeCheckpoint(int Index) const; - int IsFTimeCheckpoint(int Index) const; + int IsFrontTimeCheckpoint(int Index) const; int MoverSpeed(int x, int y, vec2 *pSpeed) const; diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 0a16aeca018..13205acb563 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1380,10 +1380,10 @@ void CCharacter::HandleSkippableTiles(int Index) Collision()->GetCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || Collision()->GetCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || Collision()->GetCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || - Collision()->GetFCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || - Collision()->GetFCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || - Collision()->GetFCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || - Collision()->GetFCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH) && + Collision()->GetFrontCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH) && !m_Core.m_Super && !m_Core.m_Invincible && !(Team() && Teams()->TeeFinished(m_pPlayer->GetCid()))) { if(Team() && Teams()->IsPractice(Team())) @@ -1506,7 +1506,7 @@ void CCharacter::HandleTiles(int Index) int MapIndex = Index; //int PureMapIndex = Collision()->GetPureMapIndex(m_Pos); m_TileIndex = Collision()->GetTileIndex(MapIndex); - m_TileFIndex = Collision()->GetFTileIndex(MapIndex); + m_TileFIndex = Collision()->GetFrontTileIndex(MapIndex); m_MoveRestrictions = Collision()->GetMoveRestrictions(IsSwitchActiveCb, this, m_Pos, 18.0f, MapIndex); if(Index < 0) { @@ -1516,7 +1516,7 @@ void CCharacter::HandleTiles(int Index) return; } SetTimeCheckpoint(Collision()->IsTimeCheckpoint(MapIndex)); - SetTimeCheckpoint(Collision()->IsFTimeCheckpoint(MapIndex)); + SetTimeCheckpoint(Collision()->IsFrontTimeCheckpoint(MapIndex)); int TeleCheckpoint = Collision()->IsTeleCheckpoint(MapIndex); if(TeleCheckpoint) m_TeleCheckpoint = TeleCheckpoint; @@ -2127,7 +2127,7 @@ void CCharacter::DDRaceTick() int Index = Collision()->GetPureMapIndex(m_Pos); const int aTiles[] = { Collision()->GetTileIndex(Index), - Collision()->GetFTileIndex(Index), + Collision()->GetFrontTileIndex(Index), Collision()->GetSwitchType(Index)}; m_Core.m_IsInFreeze = false; for(const int Tile : aTiles) diff --git a/src/game/server/entities/door.cpp b/src/game/server/entities/door.cpp index c0ad7b0f8b4..e21915f33ef 100644 --- a/src/game/server/entities/door.cpp +++ b/src/game/server/entities/door.cpp @@ -30,10 +30,10 @@ void CDoor::ResetCollision() { vec2 CurrentPos(m_Pos.x + (m_Direction.x * i), m_Pos.y + (m_Direction.y * i)); - if(GameServer()->Collision()->CheckPoint(CurrentPos) || GameServer()->Collision()->GetTile(m_Pos.x, m_Pos.y) || GameServer()->Collision()->GetFTile(m_Pos.x, m_Pos.y)) + if(GameServer()->Collision()->CheckPoint(CurrentPos) || GameServer()->Collision()->GetTile(m_Pos.x, m_Pos.y) || GameServer()->Collision()->GetFrontTile(m_Pos.x, m_Pos.y)) break; else - GameServer()->Collision()->SetDCollisionAt( + GameServer()->Collision()->SetDoorCollisionAt( m_Pos.x + (m_Direction.x * i), m_Pos.y + (m_Direction.y * i), TILE_STOPA, 0 /*Flags*/, m_Number); diff --git a/src/game/server/entities/dragger.cpp b/src/game/server/entities/dragger.cpp index 0993d01d352..9816eda140e 100644 --- a/src/game/server/entities/dragger.cpp +++ b/src/game/server/entities/dragger.cpp @@ -96,7 +96,7 @@ void CDragger::LookForPlayersToDrag() // Dragger beams can be created only for reachable, alive players int IsReachable = m_IgnoreWalls ? - !GameServer()->Collision()->IntersectNoLaserNW(m_Pos, pTarget->m_Pos, 0, 0) : + !GameServer()->Collision()->IntersectNoLaserNoWalls(m_Pos, pTarget->m_Pos, 0, 0) : !GameServer()->Collision()->IntersectNoLaser(m_Pos, pTarget->m_Pos, 0, 0); if(IsReachable && pTarget->IsAlive()) { diff --git a/src/game/server/entities/dragger_beam.cpp b/src/game/server/entities/dragger_beam.cpp index 5dace37f676..91e4588efd4 100644 --- a/src/game/server/entities/dragger_beam.cpp +++ b/src/game/server/entities/dragger_beam.cpp @@ -61,7 +61,7 @@ void CDraggerBeam::Tick() // When the dragger can no longer reach the target player, the dragger beam dissolves int IsReachable = m_IgnoreWalls ? - !GameServer()->Collision()->IntersectNoLaserNW(m_Pos, pTarget->m_Pos, 0, 0) : + !GameServer()->Collision()->IntersectNoLaserNoWalls(m_Pos, pTarget->m_Pos, 0, 0) : !GameServer()->Collision()->IntersectNoLaser(m_Pos, pTarget->m_Pos, 0, 0); if(!IsReachable || distance(pTarget->m_Pos, m_Pos) >= g_Config.m_SvDraggerRange || !pTarget->IsAlive()) diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index ba15d94f5ac..ce535d20e91 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -228,7 +228,7 @@ void CLaser::DoBounce() else if(m_Owner >= 0) { int MapIndex = GameServer()->Collision()->GetPureMapIndex(Coltile); - int TileFIndex = GameServer()->Collision()->GetFTileIndex(MapIndex); + int TileFIndex = GameServer()->Collision()->GetFrontTileIndex(MapIndex); bool IsSwitchTeleGun = GameServer()->Collision()->GetSwitchType(MapIndex) == TILE_ALLOW_TELE_GUN; bool IsBlueSwitchTeleGun = GameServer()->Collision()->GetSwitchType(MapIndex) == TILE_ALLOW_BLUE_TELE_GUN; int IsTeleInWeapon = GameServer()->Collision()->IsTeleportWeapon(MapIndex); diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp index c2bdac75fd6..7331e6db066 100644 --- a/src/game/server/entities/projectile.cpp +++ b/src/game/server/entities/projectile.cpp @@ -184,7 +184,7 @@ void CProjectile::Tick() ((m_Type == WEAPON_GRENADE && pOwnerChar->HasTelegunGrenade()) || (m_Type == WEAPON_GUN && pOwnerChar->HasTelegunGun()))) { int MapIndex = GameServer()->Collision()->GetPureMapIndex(pTargetChr ? pTargetChr->m_Pos : ColPos); - int TileFIndex = GameServer()->Collision()->GetFTileIndex(MapIndex); + int TileFIndex = GameServer()->Collision()->GetFrontTileIndex(MapIndex); bool IsSwitchTeleGun = GameServer()->Collision()->GetSwitchType(MapIndex) == TILE_ALLOW_TELE_GUN; bool IsBlueSwitchTeleGun = GameServer()->Collision()->GetSwitchType(MapIndex) == TILE_ALLOW_BLUE_TELE_GUN; diff --git a/src/game/server/gamemodes/DDRace.cpp b/src/game/server/gamemodes/DDRace.cpp index a2959a885d0..7baa0f64fac 100644 --- a/src/game/server/gamemodes/DDRace.cpp +++ b/src/game/server/gamemodes/DDRace.cpp @@ -34,7 +34,7 @@ void CGameControllerDDRace::HandleCharacterTiles(CCharacter *pChr, int MapIndex) const int ClientId = pPlayer->GetCid(); int TileIndex = GameServer()->Collision()->GetTileIndex(MapIndex); - int TileFIndex = GameServer()->Collision()->GetFTileIndex(MapIndex); + int TileFIndex = GameServer()->Collision()->GetFrontTileIndex(MapIndex); //Sensitivity int S1 = GameServer()->Collision()->GetPureMapIndex(vec2(pChr->GetPos().x + pChr->GetProximityRadius() / 3.f, pChr->GetPos().y - pChr->GetProximityRadius() / 3.f)); @@ -45,10 +45,10 @@ void CGameControllerDDRace::HandleCharacterTiles(CCharacter *pChr, int MapIndex) int Tile2 = GameServer()->Collision()->GetTileIndex(S2); int Tile3 = GameServer()->Collision()->GetTileIndex(S3); int Tile4 = GameServer()->Collision()->GetTileIndex(S4); - int FTile1 = GameServer()->Collision()->GetFTileIndex(S1); - int FTile2 = GameServer()->Collision()->GetFTileIndex(S2); - int FTile3 = GameServer()->Collision()->GetFTileIndex(S3); - int FTile4 = GameServer()->Collision()->GetFTileIndex(S4); + int FTile1 = GameServer()->Collision()->GetFrontTileIndex(S1); + int FTile2 = GameServer()->Collision()->GetFrontTileIndex(S2); + int FTile3 = GameServer()->Collision()->GetFrontTileIndex(S3); + int FTile4 = GameServer()->Collision()->GetFrontTileIndex(S4); const int PlayerDDRaceState = pChr->m_DDRaceState; bool IsOnStartTile = (TileIndex == TILE_START) || (TileFIndex == TILE_START) || FTile1 == TILE_START || FTile2 == TILE_START || FTile3 == TILE_START || FTile4 == TILE_START || Tile1 == TILE_START || Tile2 == TILE_START || Tile3 == TILE_START || Tile4 == TILE_START; From e4b1380b579ca108189e9ae28e8e1f48baf2e262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 8 Dec 2024 13:25:10 +0100 Subject: [PATCH 05/40] Upgrade to zlib 1.3.1 Changes since previous version 1.2.13: - https://github.com/madler/zlib/releases/tag/v1.3 - https://github.com/madler/zlib/releases/tag/v1.3.1 --- src/engine/external/zlib/VERSION.txt | 2 +- src/engine/external/zlib/adler32.c | 32 +- src/engine/external/zlib/compress.c | 21 +- src/engine/external/zlib/crc32.c | 248 ++++------- src/engine/external/zlib/deflate.c | 612 ++++++++++++--------------- src/engine/external/zlib/deflate.h | 51 ++- src/engine/external/zlib/gzclose.c | 4 +- src/engine/external/zlib/gzguts.h | 31 +- src/engine/external/zlib/gzlib.c | 113 ++--- src/engine/external/zlib/gzread.c | 88 +--- src/engine/external/zlib/gzwrite.c | 84 +--- src/engine/external/zlib/infback.c | 30 +- src/engine/external/zlib/inffast.c | 5 +- src/engine/external/zlib/inffast.h | 2 +- src/engine/external/zlib/inflate.c | 131 ++---- src/engine/external/zlib/inftrees.c | 17 +- src/engine/external/zlib/inftrees.h | 10 +- src/engine/external/zlib/trees.c | 542 +++++++++++------------- src/engine/external/zlib/uncompr.c | 16 +- src/engine/external/zlib/zconf.h | 18 +- src/engine/external/zlib/zlib.h | 391 ++++++++--------- src/engine/external/zlib/zutil.c | 60 +-- src/engine/external/zlib/zutil.h | 47 +- 23 files changed, 1013 insertions(+), 1542 deletions(-) diff --git a/src/engine/external/zlib/VERSION.txt b/src/engine/external/zlib/VERSION.txt index 0b1f1edf110..3a3cd8cc8b0 100644 --- a/src/engine/external/zlib/VERSION.txt +++ b/src/engine/external/zlib/VERSION.txt @@ -1 +1 @@ -1.2.13 +1.3.1 diff --git a/src/engine/external/zlib/adler32.c b/src/engine/external/zlib/adler32.c index d0be4380a39..04b81d29bad 100644 --- a/src/engine/external/zlib/adler32.c +++ b/src/engine/external/zlib/adler32.c @@ -7,8 +7,6 @@ #include "zutil.h" -local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); - #define BASE 65521U /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ @@ -60,11 +58,7 @@ local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); #endif /* ========================================================================= */ -uLong ZEXPORT adler32_z(adler, buf, len) - uLong adler; - const Bytef *buf; - z_size_t len; -{ +uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, z_size_t len) { unsigned long sum2; unsigned n; @@ -131,20 +125,12 @@ uLong ZEXPORT adler32_z(adler, buf, len) } /* ========================================================================= */ -uLong ZEXPORT adler32(adler, buf, len) - uLong adler; - const Bytef *buf; - uInt len; -{ +uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) { return adler32_z(adler, buf, len); } /* ========================================================================= */ -local uLong adler32_combine_(adler1, adler2, len2) - uLong adler1; - uLong adler2; - z_off64_t len2; -{ +local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2) { unsigned long sum1; unsigned long sum2; unsigned rem; @@ -169,18 +155,10 @@ local uLong adler32_combine_(adler1, adler2, len2) } /* ========================================================================= */ -uLong ZEXPORT adler32_combine(adler1, adler2, len2) - uLong adler1; - uLong adler2; - z_off_t len2; -{ +uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) { return adler32_combine_(adler1, adler2, len2); } -uLong ZEXPORT adler32_combine64(adler1, adler2, len2) - uLong adler1; - uLong adler2; - z_off64_t len2; -{ +uLong ZEXPORT adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2) { return adler32_combine_(adler1, adler2, len2); } diff --git a/src/engine/external/zlib/compress.c b/src/engine/external/zlib/compress.c index 2ad5326c14e..f43bacf7ab9 100644 --- a/src/engine/external/zlib/compress.c +++ b/src/engine/external/zlib/compress.c @@ -19,13 +19,8 @@ memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ -int ZEXPORT compress2(dest, destLen, source, sourceLen, level) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong sourceLen; - int level; -{ +int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen, int level) { z_stream stream; int err; const uInt max = (uInt)-1; @@ -65,12 +60,8 @@ int ZEXPORT compress2(dest, destLen, source, sourceLen, level) /* =========================================================================== */ -int ZEXPORT compress(dest, destLen, source, sourceLen) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong sourceLen; -{ +int ZEXPORT compress(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen) { return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); } @@ -78,9 +69,7 @@ int ZEXPORT compress(dest, destLen, source, sourceLen) If the default memLevel or windowBits for deflateInit() is changed, then this function needs to be updated. */ -uLong ZEXPORT compressBound(sourceLen) - uLong sourceLen; -{ +uLong ZEXPORT compressBound(uLong sourceLen) { return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13; } diff --git a/src/engine/external/zlib/crc32.c b/src/engine/external/zlib/crc32.c index f8357b083f7..6c38f5c04c6 100644 --- a/src/engine/external/zlib/crc32.c +++ b/src/engine/external/zlib/crc32.c @@ -103,19 +103,6 @@ # define ARMCRC32 #endif -/* Local functions. */ -local z_crc_t multmodp OF((z_crc_t a, z_crc_t b)); -local z_crc_t x2nmodp OF((z_off64_t n, unsigned k)); - -#if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE)) - local z_word_t byte_swap OF((z_word_t word)); -#endif - -#if defined(W) && !defined(ARMCRC32) - local z_crc_t crc_word OF((z_word_t data)); - local z_word_t crc_word_big OF((z_word_t data)); -#endif - #if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE)) /* Swap the bytes in a z_word_t to convert between little and big endian. Any @@ -123,9 +110,7 @@ local z_crc_t x2nmodp OF((z_off64_t n, unsigned k)); instruction, if one is available. This assumes that word_t is either 32 bits or 64 bits. */ -local z_word_t byte_swap(word) - z_word_t word; -{ +local z_word_t byte_swap(z_word_t word) { # if W == 8 return (word & 0xff00000000000000) >> 56 | @@ -146,24 +131,77 @@ local z_word_t byte_swap(word) } #endif +#ifdef DYNAMIC_CRC_TABLE +/* ========================================================================= + * Table of powers of x for combining CRC-32s, filled in by make_crc_table() + * below. + */ + local z_crc_t FAR x2n_table[32]; +#else +/* ========================================================================= + * Tables for byte-wise and braided CRC-32 calculations, and a table of powers + * of x for combining CRC-32s, all made by make_crc_table(). + */ +# include "crc32.h" +#endif + /* CRC polynomial. */ #define POLY 0xedb88320 /* p(x) reflected, with x^32 implied */ -#ifdef DYNAMIC_CRC_TABLE +/* + Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial, + reflected. For speed, this requires that a not be zero. + */ +local z_crc_t multmodp(z_crc_t a, z_crc_t b) { + z_crc_t m, p; + + m = (z_crc_t)1 << 31; + p = 0; + for (;;) { + if (a & m) { + p ^= b; + if ((a & (m - 1)) == 0) + break; + } + m >>= 1; + b = b & 1 ? (b >> 1) ^ POLY : b >> 1; + } + return p; +} +/* + Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been + initialized. + */ +local z_crc_t x2nmodp(z_off64_t n, unsigned k) { + z_crc_t p; + + p = (z_crc_t)1 << 31; /* x^0 == 1 */ + while (n) { + if (n & 1) + p = multmodp(x2n_table[k & 31], p); + n >>= 1; + k++; + } + return p; +} + +#ifdef DYNAMIC_CRC_TABLE +/* ========================================================================= + * Build the tables for byte-wise and braided CRC-32 calculations, and a table + * of powers of x for combining CRC-32s. + */ local z_crc_t FAR crc_table[256]; -local z_crc_t FAR x2n_table[32]; -local void make_crc_table OF((void)); #ifdef W local z_word_t FAR crc_big_table[256]; local z_crc_t FAR crc_braid_table[W][256]; local z_word_t FAR crc_braid_big_table[W][256]; - local void braid OF((z_crc_t [][256], z_word_t [][256], int, int)); + local void braid(z_crc_t [][256], z_word_t [][256], int, int); #endif #ifdef MAKECRCH - local void write_table OF((FILE *, const z_crc_t FAR *, int)); - local void write_table32hi OF((FILE *, const z_word_t FAR *, int)); - local void write_table64 OF((FILE *, const z_word_t FAR *, int)); + local void write_table(FILE *, const z_crc_t FAR *, int); + local void write_table32hi(FILE *, const z_word_t FAR *, int); + local void write_table64(FILE *, const z_word_t FAR *, int); #endif /* MAKECRCH */ /* @@ -176,7 +214,6 @@ local void make_crc_table OF((void)); /* Definition of once functionality. */ typedef struct once_s once_t; -local void once OF((once_t *, void (*)(void))); /* Check for the availability of atomics. */ #if defined(__STDC__) && __STDC_VERSION__ >= 201112L && \ @@ -196,10 +233,7 @@ struct once_s { invoke once() at the same time. The state must be a once_t initialized with ONCE_INIT. */ -local void once(state, init) - once_t *state; - void (*init)(void); -{ +local void once(once_t *state, void (*init)(void)) { if (!atomic_load(&state->done)) { if (atomic_flag_test_and_set(&state->begun)) while (!atomic_load(&state->done)) @@ -222,10 +256,7 @@ struct once_s { /* Test and set. Alas, not atomic, but tries to minimize the period of vulnerability. */ -local int test_and_set OF((int volatile *)); -local int test_and_set(flag) - int volatile *flag; -{ +local int test_and_set(int volatile *flag) { int was; was = *flag; @@ -234,10 +265,7 @@ local int test_and_set(flag) } /* Run the provided init() function once. This is not thread-safe. */ -local void once(state, init) - once_t *state; - void (*init)(void); -{ +local void once(once_t *state, void (*init)(void)) { if (!state->done) { if (test_and_set(&state->begun)) while (!state->done) @@ -279,8 +307,7 @@ local once_t made = ONCE_INIT; combinations of CRC register values and incoming bytes. */ -local void make_crc_table() -{ +local void make_crc_table(void) { unsigned i, j, n; z_crc_t p; @@ -447,11 +474,7 @@ local void make_crc_table() Write the 32-bit values in table[0..k-1] to out, five per line in hexadecimal separated by commas. */ -local void write_table(out, table, k) - FILE *out; - const z_crc_t FAR *table; - int k; -{ +local void write_table(FILE *out, const z_crc_t FAR *table, int k) { int n; for (n = 0; n < k; n++) @@ -464,11 +487,7 @@ local void write_table(out, table, k) Write the high 32-bits of each value in table[0..k-1] to out, five per line in hexadecimal separated by commas. */ -local void write_table32hi(out, table, k) -FILE *out; -const z_word_t FAR *table; -int k; -{ +local void write_table32hi(FILE *out, const z_word_t FAR *table, int k) { int n; for (n = 0; n < k; n++) @@ -484,11 +503,7 @@ int k; bits. If not, then the type cast and format string can be adjusted accordingly. */ -local void write_table64(out, table, k) - FILE *out; - const z_word_t FAR *table; - int k; -{ +local void write_table64(FILE *out, const z_word_t FAR *table, int k) { int n; for (n = 0; n < k; n++) @@ -498,8 +513,7 @@ local void write_table64(out, table, k) } /* Actually do the deed. */ -int main() -{ +int main(void) { make_crc_table(); return 0; } @@ -511,12 +525,7 @@ int main() Generate the little and big-endian braid tables for the given n and z_word_t size w. Each array must have room for w blocks of 256 elements. */ -local void braid(ltl, big, n, w) - z_crc_t ltl[][256]; - z_word_t big[][256]; - int n; - int w; -{ +local void braid(z_crc_t ltl[][256], z_word_t big[][256], int n, int w) { int k; z_crc_t i, p, q; for (k = 0; k < w; k++) { @@ -531,69 +540,13 @@ local void braid(ltl, big, n, w) } #endif -#else /* !DYNAMIC_CRC_TABLE */ -/* ======================================================================== - * Tables for byte-wise and braided CRC-32 calculations, and a table of powers - * of x for combining CRC-32s, all made by make_crc_table(). - */ -#include "crc32.h" #endif /* DYNAMIC_CRC_TABLE */ -/* ======================================================================== - * Routines used for CRC calculation. Some are also required for the table - * generation above. - */ - -/* - Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial, - reflected. For speed, this requires that a not be zero. - */ -local z_crc_t multmodp(a, b) - z_crc_t a; - z_crc_t b; -{ - z_crc_t m, p; - - m = (z_crc_t)1 << 31; - p = 0; - for (;;) { - if (a & m) { - p ^= b; - if ((a & (m - 1)) == 0) - break; - } - m >>= 1; - b = b & 1 ? (b >> 1) ^ POLY : b >> 1; - } - return p; -} - -/* - Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been - initialized. - */ -local z_crc_t x2nmodp(n, k) - z_off64_t n; - unsigned k; -{ - z_crc_t p; - - p = (z_crc_t)1 << 31; /* x^0 == 1 */ - while (n) { - if (n & 1) - p = multmodp(x2n_table[k & 31], p); - n >>= 1; - k++; - } - return p; -} - /* ========================================================================= * This function can be used by asm versions of crc32(), and to force the * generation of the CRC tables in a threaded application. */ -const z_crc_t FAR * ZEXPORT get_crc_table() -{ +const z_crc_t FAR * ZEXPORT get_crc_table(void) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ @@ -619,11 +572,8 @@ const z_crc_t FAR * ZEXPORT get_crc_table() #define Z_BATCH_ZEROS 0xa10d3d0c /* computed from Z_BATCH = 3990 */ #define Z_BATCH_MIN 800 /* fewest words in a final batch */ -unsigned long ZEXPORT crc32_z(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - z_size_t len; -{ +unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf, + z_size_t len) { z_crc_t val; z_word_t crc1, crc2; const z_word_t *word; @@ -723,18 +673,14 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) least-significant byte of the word as the first byte of data, without any pre or post conditioning. This is used to combine the CRCs of each braid. */ -local z_crc_t crc_word(data) - z_word_t data; -{ +local z_crc_t crc_word(z_word_t data) { int k; for (k = 0; k < W; k++) data = (data >> 8) ^ crc_table[data & 0xff]; return (z_crc_t)data; } -local z_word_t crc_word_big(data) - z_word_t data; -{ +local z_word_t crc_word_big(z_word_t data) { int k; for (k = 0; k < W; k++) data = (data << 8) ^ @@ -745,11 +691,8 @@ local z_word_t crc_word_big(data) #endif /* ========================================================================= */ -unsigned long ZEXPORT crc32_z(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - z_size_t len; -{ +unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf, + z_size_t len) { /* Return initial CRC, if requested. */ if (buf == Z_NULL) return 0; @@ -781,8 +724,8 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) words = (z_word_t const *)buf; /* Do endian check at execution time instead of compile time, since ARM - processors can change the endianess at execution time. If the - compiler knows what the endianess will be, it can optimize out the + processors can change the endianness at execution time. If the + compiler knows what the endianness will be, it can optimize out the check and the unused branch. */ endian = 1; if (*(unsigned char *)&endian) { @@ -1069,20 +1012,13 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) #endif /* ========================================================================= */ -unsigned long ZEXPORT crc32(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - uInt len; -{ +unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, + uInt len) { return crc32_z(crc, buf, len); } /* ========================================================================= */ -uLong ZEXPORT crc32_combine64(crc1, crc2, len2) - uLong crc1; - uLong crc2; - z_off64_t len2; -{ +uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ @@ -1090,18 +1026,12 @@ uLong ZEXPORT crc32_combine64(crc1, crc2, len2) } /* ========================================================================= */ -uLong ZEXPORT crc32_combine(crc1, crc2, len2) - uLong crc1; - uLong crc2; - z_off_t len2; -{ +uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) { return crc32_combine64(crc1, crc2, (z_off64_t)len2); } /* ========================================================================= */ -uLong ZEXPORT crc32_combine_gen64(len2) - z_off64_t len2; -{ +uLong ZEXPORT crc32_combine_gen64(z_off64_t len2) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ @@ -1109,17 +1039,11 @@ uLong ZEXPORT crc32_combine_gen64(len2) } /* ========================================================================= */ -uLong ZEXPORT crc32_combine_gen(len2) - z_off_t len2; -{ +uLong ZEXPORT crc32_combine_gen(z_off_t len2) { return crc32_combine_gen64((z_off64_t)len2); } /* ========================================================================= */ -uLong ZEXPORT crc32_combine_op(crc1, crc2, op) - uLong crc1; - uLong crc2; - uLong op; -{ +uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op) { return multmodp(op, crc1) ^ (crc2 & 0xffffffff); } diff --git a/src/engine/external/zlib/deflate.c b/src/engine/external/zlib/deflate.c index 4a689db3598..012ea8148e8 100644 --- a/src/engine/external/zlib/deflate.c +++ b/src/engine/external/zlib/deflate.c @@ -1,5 +1,5 @@ /* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler + * Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -52,7 +52,7 @@ #include "deflate.h" const char deflate_copyright[] = - " deflate 1.2.13 Copyright 1995-2022 Jean-loup Gailly and Mark Adler "; + " deflate 1.3.1 Copyright 1995-2024 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -60,9 +60,6 @@ const char deflate_copyright[] = copyright string in the executable of your product. */ -/* =========================================================================== - * Function prototypes. - */ typedef enum { need_more, /* block not completed, need more input or more output */ block_done, /* block flush performed */ @@ -70,29 +67,16 @@ typedef enum { finish_done /* finish done, accept no more input or output */ } block_state; -typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +typedef block_state (*compress_func)(deflate_state *s, int flush); /* Compression function. Returns the block state after the call. */ -local int deflateStateCheck OF((z_streamp strm)); -local void slide_hash OF((deflate_state *s)); -local void fill_window OF((deflate_state *s)); -local block_state deflate_stored OF((deflate_state *s, int flush)); -local block_state deflate_fast OF((deflate_state *s, int flush)); +local block_state deflate_stored(deflate_state *s, int flush); +local block_state deflate_fast(deflate_state *s, int flush); #ifndef FASTEST -local block_state deflate_slow OF((deflate_state *s, int flush)); -#endif -local block_state deflate_rle OF((deflate_state *s, int flush)); -local block_state deflate_huff OF((deflate_state *s, int flush)); -local void lm_init OF((deflate_state *s)); -local void putShortMSB OF((deflate_state *s, uInt b)); -local void flush_pending OF((z_streamp strm)); -local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); -local uInt longest_match OF((deflate_state *s, IPos cur_match)); - -#ifdef ZLIB_DEBUG -local void check_match OF((deflate_state *s, IPos start, IPos match, - int length)); +local block_state deflate_slow(deflate_state *s, int flush); #endif +local block_state deflate_rle(deflate_state *s, int flush); +local block_state deflate_huff(deflate_state *s, int flush); /* =========================================================================== * Local data @@ -195,9 +179,12 @@ local const config configuration_table[10] = { * bit values at the expense of memory usage). We slide even when level == 0 to * keep the hash table consistent if we switch back to level > 0 later. */ -local void slide_hash(s) - deflate_state *s; -{ +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) + __attribute__((no_sanitize("memory"))) +# endif +#endif +local void slide_hash(deflate_state *s) { unsigned n, m; Posf *p; uInt wsize = s->w_size; @@ -221,30 +208,177 @@ local void slide_hash(s) #endif } +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local unsigned read_buf(z_streamp strm, Bytef *buf, unsigned size) { + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + zmemcpy(buf, strm->next_in, len); + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, buf, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, buf, len); + } +#endif + strm->next_in += len; + strm->total_in += len; + + return len; +} + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(deflate_state *s) { + unsigned n; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize + MAX_DIST(s)) { + + zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + if (s->insert > s->strstart) + s->insert = s->strstart; + slide_hash(s); + more += wsize; + } + if (s->strm->avail_in == 0) break; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead + s->insert >= MIN_MATCH) { + uInt str = s->strstart - s->insert; + s->ins_h = s->window[str]; + UPDATE_HASH(s, s->ins_h, s->window[str + 1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + while (s->insert) { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + s->insert--; + if (s->lookahead + s->insert < MIN_MATCH) + break; + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } + + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "not enough room for search"); +} + /* ========================================================================= */ -int ZEXPORT deflateInit_(strm, level, version, stream_size) - z_streamp strm; - int level; - const char *version; - int stream_size; -{ +int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version, + int stream_size) { return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, version, stream_size); /* To do: ignore strm->next_in if we use it as window */ } /* ========================================================================= */ -int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, - version, stream_size) - z_streamp strm; - int level; - int method; - int windowBits; - int memLevel; - int strategy; - const char *version; - int stream_size; -{ +int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, + int windowBits, int memLevel, int strategy, + const char *version, int stream_size) { deflate_state *s; int wrap = 1; static const char my_version[] = ZLIB_VERSION; @@ -359,7 +493,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, * symbols from which it is being constructed. */ - s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, 4); + s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, LIT_BUFS); s->pending_buf_size = (ulg)s->lit_bufsize * 4; if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || @@ -369,8 +503,14 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, deflateEnd (strm); return Z_MEM_ERROR; } +#ifdef LIT_MEM + s->d_buf = (ushf *)(s->pending_buf + (s->lit_bufsize << 1)); + s->l_buf = s->pending_buf + (s->lit_bufsize << 2); + s->sym_end = s->lit_bufsize - 1; +#else s->sym_buf = s->pending_buf + s->lit_bufsize; s->sym_end = (s->lit_bufsize - 1) * 3; +#endif /* We avoid equality with lit_bufsize*3 because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. @@ -386,9 +526,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, /* ========================================================================= * Check for a valid deflate stream state. Return 0 if ok, 1 if not. */ -local int deflateStateCheck(strm) - z_streamp strm; -{ +local int deflateStateCheck(z_streamp strm) { deflate_state *s; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) @@ -409,11 +547,8 @@ local int deflateStateCheck(strm) } /* ========================================================================= */ -int ZEXPORT deflateSetDictionary(strm, dictionary, dictLength) - z_streamp strm; - const Bytef *dictionary; - uInt dictLength; -{ +int ZEXPORT deflateSetDictionary(z_streamp strm, const Bytef *dictionary, + uInt dictLength) { deflate_state *s; uInt str, n; int wrap; @@ -478,11 +613,8 @@ int ZEXPORT deflateSetDictionary(strm, dictionary, dictLength) } /* ========================================================================= */ -int ZEXPORT deflateGetDictionary(strm, dictionary, dictLength) - z_streamp strm; - Bytef *dictionary; - uInt *dictLength; -{ +int ZEXPORT deflateGetDictionary(z_streamp strm, Bytef *dictionary, + uInt *dictLength) { deflate_state *s; uInt len; @@ -500,9 +632,7 @@ int ZEXPORT deflateGetDictionary(strm, dictionary, dictLength) } /* ========================================================================= */ -int ZEXPORT deflateResetKeep(strm) - z_streamp strm; -{ +int ZEXPORT deflateResetKeep(z_streamp strm) { deflate_state *s; if (deflateStateCheck(strm)) { @@ -537,10 +667,32 @@ int ZEXPORT deflateResetKeep(strm) return Z_OK; } +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init(deflate_state *s) { + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->insert = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +} + /* ========================================================================= */ -int ZEXPORT deflateReset(strm) - z_streamp strm; -{ +int ZEXPORT deflateReset(z_streamp strm) { int ret; ret = deflateResetKeep(strm); @@ -550,10 +702,7 @@ int ZEXPORT deflateReset(strm) } /* ========================================================================= */ -int ZEXPORT deflateSetHeader(strm, head) - z_streamp strm; - gz_headerp head; -{ +int ZEXPORT deflateSetHeader(z_streamp strm, gz_headerp head) { if (deflateStateCheck(strm) || strm->state->wrap != 2) return Z_STREAM_ERROR; strm->state->gzhead = head; @@ -561,11 +710,7 @@ int ZEXPORT deflateSetHeader(strm, head) } /* ========================================================================= */ -int ZEXPORT deflatePending(strm, pending, bits) - unsigned *pending; - int *bits; - z_streamp strm; -{ +int ZEXPORT deflatePending(z_streamp strm, unsigned *pending, int *bits) { if (deflateStateCheck(strm)) return Z_STREAM_ERROR; if (pending != Z_NULL) *pending = strm->state->pending; @@ -575,19 +720,21 @@ int ZEXPORT deflatePending(strm, pending, bits) } /* ========================================================================= */ -int ZEXPORT deflatePrime(strm, bits, value) - z_streamp strm; - int bits; - int value; -{ +int ZEXPORT deflatePrime(z_streamp strm, int bits, int value) { deflate_state *s; int put; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; +#ifdef LIT_MEM + if (bits < 0 || bits > 16 || + (uchf *)s->d_buf < s->pending_out + ((Buf_size + 7) >> 3)) + return Z_BUF_ERROR; +#else if (bits < 0 || bits > 16 || s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; +#endif do { put = Buf_size - s->bi_valid; if (put > bits) @@ -602,11 +749,7 @@ int ZEXPORT deflatePrime(strm, bits, value) } /* ========================================================================= */ -int ZEXPORT deflateParams(strm, level, strategy) - z_streamp strm; - int level; - int strategy; -{ +int ZEXPORT deflateParams(z_streamp strm, int level, int strategy) { deflate_state *s; compress_func func; @@ -651,13 +794,8 @@ int ZEXPORT deflateParams(strm, level, strategy) } /* ========================================================================= */ -int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) - z_streamp strm; - int good_length; - int max_lazy; - int nice_length; - int max_chain; -{ +int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy, + int nice_length, int max_chain) { deflate_state *s; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -693,10 +831,7 @@ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) * * Shifts are used to approximate divisions, for speed. */ -uLong ZEXPORT deflateBound(strm, sourceLen) - z_streamp strm; - uLong sourceLen; -{ +uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) { deflate_state *s; uLong fixedlen, storelen, wraplen; @@ -752,7 +887,8 @@ uLong ZEXPORT deflateBound(strm, sourceLen) /* if not default parameters, return one of the conservative bounds */ if (s->w_bits != 15 || s->hash_bits != 8 + 7) - return (s->w_bits <= s->hash_bits ? fixedlen : storelen) + wraplen; + return (s->w_bits <= s->hash_bits && s->level ? fixedlen : storelen) + + wraplen; /* default settings: return tight bound for that case -- ~0.03% overhead plus a small constant */ @@ -765,10 +901,7 @@ uLong ZEXPORT deflateBound(strm, sourceLen) * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ -local void putShortMSB(s, b) - deflate_state *s; - uInt b; -{ +local void putShortMSB(deflate_state *s, uInt b) { put_byte(s, (Byte)(b >> 8)); put_byte(s, (Byte)(b & 0xff)); } @@ -779,9 +912,7 @@ local void putShortMSB(s, b) * applications may wish to modify it to avoid allocating a large * strm->next_out buffer and copying into it. (See also read_buf()). */ -local void flush_pending(strm) - z_streamp strm; -{ +local void flush_pending(z_streamp strm) { unsigned len; deflate_state *s = strm->state; @@ -812,10 +943,7 @@ local void flush_pending(strm) } while (0) /* ========================================================================= */ -int ZEXPORT deflate(strm, flush) - z_streamp strm; - int flush; -{ +int ZEXPORT deflate(z_streamp strm, int flush) { int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; @@ -1127,9 +1255,7 @@ int ZEXPORT deflate(strm, flush) } /* ========================================================================= */ -int ZEXPORT deflateEnd(strm) - z_streamp strm; -{ +int ZEXPORT deflateEnd(z_streamp strm) { int status; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1153,11 +1279,10 @@ int ZEXPORT deflateEnd(strm) * To simplify the source, this is not supported for 16-bit MSDOS (which * doesn't have enough memory anyway to duplicate compression states). */ -int ZEXPORT deflateCopy(dest, source) - z_streamp dest; - z_streamp source; -{ +int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) { #ifdef MAXSEG_64K + (void)dest; + (void)source; return Z_STREAM_ERROR; #else deflate_state *ds; @@ -1181,7 +1306,7 @@ int ZEXPORT deflateCopy(dest, source) ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); - ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, 4); + ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, LIT_BUFS); if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || ds->pending_buf == Z_NULL) { @@ -1192,10 +1317,15 @@ int ZEXPORT deflateCopy(dest, source) zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); - zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + zmemcpy(ds->pending_buf, ss->pending_buf, ds->lit_bufsize * LIT_BUFS); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); +#ifdef LIT_MEM + ds->d_buf = (ushf *)(ds->pending_buf + (ds->lit_bufsize << 1)); + ds->l_buf = ds->pending_buf + (ds->lit_bufsize << 2); +#else ds->sym_buf = ds->pending_buf + ds->lit_bufsize; +#endif ds->l_desc.dyn_tree = ds->dyn_ltree; ds->d_desc.dyn_tree = ds->dyn_dtree; @@ -1205,66 +1335,6 @@ int ZEXPORT deflateCopy(dest, source) #endif /* MAXSEG_64K */ } -/* =========================================================================== - * Read a new buffer from the current input stream, update the adler32 - * and total number of bytes read. All deflate() input goes through - * this function so some applications may wish to modify it to avoid - * allocating a large strm->next_in buffer and copying from it. - * (See also flush_pending()). - */ -local unsigned read_buf(strm, buf, size) - z_streamp strm; - Bytef *buf; - unsigned size; -{ - unsigned len = strm->avail_in; - - if (len > size) len = size; - if (len == 0) return 0; - - strm->avail_in -= len; - - zmemcpy(buf, strm->next_in, len); - if (strm->state->wrap == 1) { - strm->adler = adler32(strm->adler, buf, len); - } -#ifdef GZIP - else if (strm->state->wrap == 2) { - strm->adler = crc32(strm->adler, buf, len); - } -#endif - strm->next_in += len; - strm->total_in += len; - - return len; -} - -/* =========================================================================== - * Initialize the "longest match" routines for a new zlib stream - */ -local void lm_init(s) - deflate_state *s; -{ - s->window_size = (ulg)2L*s->w_size; - - CLEAR_HASH(s); - - /* Set the default configuration parameters: - */ - s->max_lazy_match = configuration_table[s->level].max_lazy; - s->good_match = configuration_table[s->level].good_length; - s->nice_match = configuration_table[s->level].nice_length; - s->max_chain_length = configuration_table[s->level].max_chain; - - s->strstart = 0; - s->block_start = 0L; - s->lookahead = 0; - s->insert = 0; - s->match_length = s->prev_length = MIN_MATCH-1; - s->match_available = 0; - s->ins_h = 0; -} - #ifndef FASTEST /* =========================================================================== * Set match_start to the longest match starting at the given string and @@ -1275,10 +1345,7 @@ local void lm_init(s) * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ -local uInt longest_match(s, cur_match) - deflate_state *s; - IPos cur_match; /* current match */ -{ +local uInt longest_match(deflate_state *s, IPos cur_match) { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ @@ -1426,10 +1493,7 @@ local uInt longest_match(s, cur_match) /* --------------------------------------------------------------------------- * Optimized version for FASTEST only */ -local uInt longest_match(s, cur_match) - deflate_state *s; - IPos cur_match; /* current match */ -{ +local uInt longest_match(deflate_state *s, IPos cur_match) { register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ @@ -1490,19 +1554,23 @@ local uInt longest_match(s, cur_match) /* =========================================================================== * Check that the match at match_start is indeed a match. */ -local void check_match(s, start, match, length) - deflate_state *s; - IPos start, match; - int length; -{ +local void check_match(deflate_state *s, IPos start, IPos match, int length) { /* check that the match is indeed a match */ - if (zmemcmp(s->window + match, - s->window + start, length) != EQUAL) { - fprintf(stderr, " start %u, match %u, length %d\n", - start, match, length); + Bytef *back = s->window + (int)match, *here = s->window + start; + IPos len = length; + if (match == (IPos)-1) { + /* match starts one byte before the current window -- just compare the + subsequent length-1 bytes */ + back++; + here++; + len--; + } + if (zmemcmp(back, here, len) != EQUAL) { + fprintf(stderr, " start %u, match %d, length %d\n", + start, (int)match, length); do { - fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); - } while (--length != 0); + fprintf(stderr, "(%02x %02x)", *back++, *here++); + } while (--len != 0); z_error("invalid match"); } if (z_verbose > 1) { @@ -1514,137 +1582,6 @@ local void check_match(s, start, match, length) # define check_match(s, start, match, length) #endif /* ZLIB_DEBUG */ -/* =========================================================================== - * Fill the window when the lookahead becomes insufficient. - * Updates strstart and lookahead. - * - * IN assertion: lookahead < MIN_LOOKAHEAD - * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - * At least one byte has been read, or avail_in == 0; reads are - * performed for at least two bytes (required for the zip translate_eol - * option -- not supported here). - */ -local void fill_window(s) - deflate_state *s; -{ - unsigned n; - unsigned more; /* Amount of free space at the end of the window. */ - uInt wsize = s->w_size; - - Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); - - /* Deal with !@#$% 64K limit: */ - if (sizeof(int) <= 2) { - if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - more = wsize; - - } else if (more == (unsigned)(-1)) { - /* Very unlikely, but possible on 16 bit machine if - * strstart == 0 && lookahead == 1 (input done a byte at time) - */ - more--; - } - } - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - if (s->strstart >= wsize + MAX_DIST(s)) { - - zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more); - s->match_start -= wsize; - s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ - s->block_start -= (long) wsize; - if (s->insert > s->strstart) - s->insert = s->strstart; - slide_hash(s); - more += wsize; - } - if (s->strm->avail_in == 0) break; - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - Assert(more >= 2, "more < 2"); - - n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); - s->lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s->lookahead + s->insert >= MIN_MATCH) { - uInt str = s->strstart - s->insert; - s->ins_h = s->window[str]; - UPDATE_HASH(s, s->ins_h, s->window[str + 1]); -#if MIN_MATCH != 3 - Call UPDATE_HASH() MIN_MATCH-3 more times -#endif - while (s->insert) { - UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); -#ifndef FASTEST - s->prev[str & s->w_mask] = s->head[s->ins_h]; -#endif - s->head[s->ins_h] = (Pos)str; - str++; - s->insert--; - if (s->lookahead + s->insert < MIN_MATCH) - break; - } - } - /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ - if (s->high_water < s->window_size) { - ulg curr = s->strstart + (ulg)(s->lookahead); - ulg init; - - if (s->high_water < curr) { - /* Previous high water mark below current data -- zero WIN_INIT - * bytes or up to end of window, whichever is less. - */ - init = s->window_size - curr; - if (init > WIN_INIT) - init = WIN_INIT; - zmemzero(s->window + curr, (unsigned)init); - s->high_water = curr + init; - } - else if (s->high_water < (ulg)curr + WIN_INIT) { - /* High water mark at or above current data, but below current data - * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up - * to end of window, whichever is less. - */ - init = (ulg)curr + WIN_INIT - s->high_water; - if (init > s->window_size - s->high_water) - init = s->window_size - s->high_water; - zmemzero(s->window + s->high_water, (unsigned)init); - s->high_water += init; - } - } - - Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, - "not enough room for search"); -} - /* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. @@ -1687,10 +1624,7 @@ local void fill_window(s) * copied. It is most efficient with large input and output buffers, which * maximizes the opportunities to have a single copy from next_in to next_out. */ -local block_state deflate_stored(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_stored(deflate_state *s, int flush) { /* Smallest worthy block size when not flushing or finishing. By default * this is 32K. This can be as small as 507 bytes for memLevel == 1. For * large input and output buffers, the stored block size will be larger. @@ -1874,10 +1808,7 @@ local block_state deflate_stored(s, flush) * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ -local block_state deflate_fast(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_fast(deflate_state *s, int flush) { IPos hash_head; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ @@ -1976,10 +1907,7 @@ local block_state deflate_fast(s, flush) * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ -local block_state deflate_slow(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_slow(deflate_state *s, int flush) { IPos hash_head; /* head of hash chain */ int bflush; /* set if current block must be flushed */ @@ -2107,10 +2035,7 @@ local block_state deflate_slow(s, flush) * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ -local block_state deflate_rle(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_rle(deflate_state *s, int flush) { int bflush; /* set if current block must be flushed */ uInt prev; /* byte at distance one to match */ Bytef *scan, *strend; /* scan goes up to strend for length of run */ @@ -2181,10 +2106,7 @@ local block_state deflate_rle(s, flush) * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ -local block_state deflate_huff(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_huff(deflate_state *s, int flush) { int bflush; /* set if current block must be flushed */ for (;;) { diff --git a/src/engine/external/zlib/deflate.h b/src/engine/external/zlib/deflate.h index 1a06cd5f25d..300c6ada62b 100644 --- a/src/engine/external/zlib/deflate.h +++ b/src/engine/external/zlib/deflate.h @@ -1,5 +1,5 @@ /* deflate.h -- internal compression state - * Copyright (C) 1995-2018 Jean-loup Gailly + * Copyright (C) 1995-2024 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -23,6 +23,10 @@ # define GZIP #endif +/* define LIT_MEM to slightly increase the speed of deflate (order 1% to 2%) at + the cost of a larger memory footprint */ +/* #define LIT_MEM */ + /* =========================================================================== * Internal compression state. */ @@ -217,7 +221,14 @@ typedef struct internal_state { /* Depth of each subtree used as tie breaker for trees of equal frequency */ +#ifdef LIT_MEM +# define LIT_BUFS 5 + ushf *d_buf; /* buffer for distances */ + uchf *l_buf; /* buffer for literals/lengths */ +#else +# define LIT_BUFS 4 uchf *sym_buf; /* buffer for distances and literals/lengths */ +#endif uInt lit_bufsize; /* Size of match buffer for literals/lengths. There are 4 reasons for @@ -239,7 +250,7 @@ typedef struct internal_state { * - I can't count above 4 */ - uInt sym_next; /* running index in sym_buf */ + uInt sym_next; /* running index in symbol buffer */ uInt sym_end; /* symbol table full when sym_next reaches this */ ulg opt_len; /* bit length of current block with optimal trees */ @@ -291,14 +302,14 @@ typedef struct internal_state { memory checker errors from longest match routines */ /* in trees.c */ -void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); -int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); -void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, - ulg stored_len, int last)); -void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); -void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); -void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, - ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_init(deflate_state *s); +int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc); +void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, + ulg stored_len, int last); +void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s); +void ZLIB_INTERNAL _tr_align(deflate_state *s); +void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, + ulg stored_len, int last); #define d_code(dist) \ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) @@ -318,6 +329,25 @@ void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, extern const uch ZLIB_INTERNAL _dist_code[]; #endif +#ifdef LIT_MEM +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->sym_next] = 0; \ + s->l_buf[s->sym_next++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->sym_next == s->sym_end); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (uch)(length); \ + ush dist = (ush)(distance); \ + s->d_buf[s->sym_next] = dist; \ + s->l_buf[s->sym_next++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->sym_next == s->sym_end); \ + } +#else # define _tr_tally_lit(s, c, flush) \ { uch cc = (c); \ s->sym_buf[s->sym_next++] = 0; \ @@ -337,6 +367,7 @@ void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, s->dyn_dtree[d_code(dist)].Freq++; \ flush = (s->sym_next == s->sym_end); \ } +#endif #else # define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) # define _tr_tally_dist(s, distance, length, flush) \ diff --git a/src/engine/external/zlib/gzclose.c b/src/engine/external/zlib/gzclose.c index caeb99a3177..48d6a86f04b 100644 --- a/src/engine/external/zlib/gzclose.c +++ b/src/engine/external/zlib/gzclose.c @@ -8,9 +8,7 @@ /* gzclose() is in a separate file so that it is linked in only if it is used. That way the other gzclose functions can be used instead to avoid linking in unneeded compression or decompression routines. */ -int ZEXPORT gzclose(file) - gzFile file; -{ +int ZEXPORT gzclose(gzFile file) { #ifndef NO_GZCOMPRESS gz_statep state; diff --git a/src/engine/external/zlib/gzguts.h b/src/engine/external/zlib/gzguts.h index 57faf37165a..eba72085bb7 100644 --- a/src/engine/external/zlib/gzguts.h +++ b/src/engine/external/zlib/gzguts.h @@ -1,5 +1,5 @@ /* gzguts.h -- zlib internal header definitions for gz* operations - * Copyright (C) 2004-2019 Mark Adler + * Copyright (C) 2004-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -7,9 +7,8 @@ # ifndef _LARGEFILE_SOURCE # define _LARGEFILE_SOURCE 1 # endif -# ifdef _FILE_OFFSET_BITS -# undef _FILE_OFFSET_BITS -# endif +# undef _FILE_OFFSET_BITS +# undef _TIME_BITS #endif #ifdef HAVE_HIDDEN @@ -119,8 +118,8 @@ /* gz* functions always use library allocation functions */ #ifndef STDC - extern voidp malloc OF((uInt size)); - extern void free OF((voidpf ptr)); + extern voidp malloc(uInt size); + extern void free(voidpf ptr); #endif /* get errno and strerror definition */ @@ -138,10 +137,10 @@ /* provide prototypes for these when building zlib without LFS */ #if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); - ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); + ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); #endif /* default memLevel */ @@ -203,17 +202,13 @@ typedef struct { typedef gz_state FAR *gz_statep; /* shared functions */ -void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +void ZLIB_INTERNAL gz_error(gz_statep, int, const char *); #if defined UNDER_CE -char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +char ZLIB_INTERNAL *gz_strwinerror(DWORD error); #endif /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t value -- needed when comparing unsigned to z_off64_t, which is signed (possible z_off64_t types off_t, off64_t, and long are all signed) */ -#ifdef INT_MAX -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) -#else -unsigned ZLIB_INTERNAL gz_intmax OF((void)); -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) -#endif +unsigned ZLIB_INTERNAL gz_intmax(void); +#define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) diff --git a/src/engine/external/zlib/gzlib.c b/src/engine/external/zlib/gzlib.c index 55da46a453f..983153cc8e4 100644 --- a/src/engine/external/zlib/gzlib.c +++ b/src/engine/external/zlib/gzlib.c @@ -1,5 +1,5 @@ /* gzlib.c -- zlib functions common to reading and writing gzip files - * Copyright (C) 2004-2019 Mark Adler + * Copyright (C) 2004-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -15,10 +15,6 @@ #endif #endif -/* Local functions */ -local void gz_reset OF((gz_statep)); -local gzFile gz_open OF((const void *, int, const char *)); - #if defined UNDER_CE /* Map the Windows error number in ERROR to a locale-dependent error message @@ -30,9 +26,7 @@ local gzFile gz_open OF((const void *, int, const char *)); The gz_strwinerror function does not change the current setting of GetLastError. */ -char ZLIB_INTERNAL *gz_strwinerror(error) - DWORD error; -{ +char ZLIB_INTERNAL *gz_strwinerror(DWORD error) { static char buf[1024]; wchar_t *msgbuf; @@ -72,9 +66,7 @@ char ZLIB_INTERNAL *gz_strwinerror(error) #endif /* UNDER_CE */ /* Reset gzip file state */ -local void gz_reset(state) - gz_statep state; -{ +local void gz_reset(gz_statep state) { state->x.have = 0; /* no output data available */ if (state->mode == GZ_READ) { /* for reading ... */ state->eof = 0; /* not at end of file */ @@ -90,11 +82,7 @@ local void gz_reset(state) } /* Open a gzip file either by name or file descriptor. */ -local gzFile gz_open(path, fd, mode) - const void *path; - int fd; - const char *mode; -{ +local gzFile gz_open(const void *path, int fd, const char *mode) { gz_statep state; z_size_t len; int oflag; @@ -269,26 +257,17 @@ local gzFile gz_open(path, fd, mode) } /* -- see zlib.h -- */ -gzFile ZEXPORT gzopen(path, mode) - const char *path; - const char *mode; -{ +gzFile ZEXPORT gzopen(const char *path, const char *mode) { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ -gzFile ZEXPORT gzopen64(path, mode) - const char *path; - const char *mode; -{ +gzFile ZEXPORT gzopen64(const char *path, const char *mode) { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ -gzFile ZEXPORT gzdopen(fd, mode) - int fd; - const char *mode; -{ +gzFile ZEXPORT gzdopen(int fd, const char *mode) { char *path; /* identifier for error messages */ gzFile gz; @@ -306,19 +285,13 @@ gzFile ZEXPORT gzdopen(fd, mode) /* -- see zlib.h -- */ #ifdef WIDECHAR -gzFile ZEXPORT gzopen_w(path, mode) - const wchar_t *path; - const char *mode; -{ +gzFile ZEXPORT gzopen_w(const wchar_t *path, const char *mode) { return gz_open(path, -2, mode); } #endif /* -- see zlib.h -- */ -int ZEXPORT gzbuffer(file, size) - gzFile file; - unsigned size; -{ +int ZEXPORT gzbuffer(gzFile file, unsigned size) { gz_statep state; /* get internal structure and check integrity */ @@ -335,16 +308,14 @@ int ZEXPORT gzbuffer(file, size) /* check and set requested size */ if ((size << 1) < size) return -1; /* need to be able to double it */ - if (size < 2) - size = 2; /* need two bytes to check magic header */ + if (size < 8) + size = 8; /* needed to behave well with flushing */ state->want = size; return 0; } /* -- see zlib.h -- */ -int ZEXPORT gzrewind(file) - gzFile file; -{ +int ZEXPORT gzrewind(gzFile file) { gz_statep state; /* get internal structure */ @@ -365,11 +336,7 @@ int ZEXPORT gzrewind(file) } /* -- see zlib.h -- */ -z_off64_t ZEXPORT gzseek64(file, offset, whence) - gzFile file; - z_off64_t offset; - int whence; -{ +z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) { unsigned n; z_off64_t ret; gz_statep state; @@ -442,11 +409,7 @@ z_off64_t ZEXPORT gzseek64(file, offset, whence) } /* -- see zlib.h -- */ -z_off_t ZEXPORT gzseek(file, offset, whence) - gzFile file; - z_off_t offset; - int whence; -{ +z_off_t ZEXPORT gzseek(gzFile file, z_off_t offset, int whence) { z_off64_t ret; ret = gzseek64(file, (z_off64_t)offset, whence); @@ -454,9 +417,7 @@ z_off_t ZEXPORT gzseek(file, offset, whence) } /* -- see zlib.h -- */ -z_off64_t ZEXPORT gztell64(file) - gzFile file; -{ +z_off64_t ZEXPORT gztell64(gzFile file) { gz_statep state; /* get internal structure and check integrity */ @@ -471,9 +432,7 @@ z_off64_t ZEXPORT gztell64(file) } /* -- see zlib.h -- */ -z_off_t ZEXPORT gztell(file) - gzFile file; -{ +z_off_t ZEXPORT gztell(gzFile file) { z_off64_t ret; ret = gztell64(file); @@ -481,9 +440,7 @@ z_off_t ZEXPORT gztell(file) } /* -- see zlib.h -- */ -z_off64_t ZEXPORT gzoffset64(file) - gzFile file; -{ +z_off64_t ZEXPORT gzoffset64(gzFile file) { z_off64_t offset; gz_statep state; @@ -504,9 +461,7 @@ z_off64_t ZEXPORT gzoffset64(file) } /* -- see zlib.h -- */ -z_off_t ZEXPORT gzoffset(file) - gzFile file; -{ +z_off_t ZEXPORT gzoffset(gzFile file) { z_off64_t ret; ret = gzoffset64(file); @@ -514,9 +469,7 @@ z_off_t ZEXPORT gzoffset(file) } /* -- see zlib.h -- */ -int ZEXPORT gzeof(file) - gzFile file; -{ +int ZEXPORT gzeof(gzFile file) { gz_statep state; /* get internal structure and check integrity */ @@ -531,10 +484,7 @@ int ZEXPORT gzeof(file) } /* -- see zlib.h -- */ -const char * ZEXPORT gzerror(file, errnum) - gzFile file; - int *errnum; -{ +const char * ZEXPORT gzerror(gzFile file, int *errnum) { gz_statep state; /* get internal structure and check integrity */ @@ -552,9 +502,7 @@ const char * ZEXPORT gzerror(file, errnum) } /* -- see zlib.h -- */ -void ZEXPORT gzclearerr(file) - gzFile file; -{ +void ZEXPORT gzclearerr(gzFile file) { gz_statep state; /* get internal structure and check integrity */ @@ -578,11 +526,7 @@ void ZEXPORT gzclearerr(file) memory). Simply save the error message as a static string. If there is an allocation failure constructing the error message, then convert the error to out of memory. */ -void ZLIB_INTERNAL gz_error(state, err, msg) - gz_statep state; - int err; - const char *msg; -{ +void ZLIB_INTERNAL gz_error(gz_statep state, int err, const char *msg) { /* free previously allocated message and clear */ if (state->msg != NULL) { if (state->err != Z_MEM_ERROR) @@ -619,21 +563,20 @@ void ZLIB_INTERNAL gz_error(state, err, msg) #endif } -#ifndef INT_MAX /* portably return maximum value for an int (when limits.h presumed not available) -- we need to do this to cover cases where 2's complement not used, since C standard permits 1's complement and sign-bit representations, otherwise we could just use ((unsigned)-1) >> 1 */ -unsigned ZLIB_INTERNAL gz_intmax() -{ - unsigned p, q; - - p = 1; +unsigned ZLIB_INTERNAL gz_intmax(void) { +#ifdef INT_MAX + return INT_MAX; +#else + unsigned p = 1, q; do { q = p; p <<= 1; p++; } while (p > q); return q >> 1; -} #endif +} diff --git a/src/engine/external/zlib/gzread.c b/src/engine/external/zlib/gzread.c index dd77381596c..4168cbc8875 100644 --- a/src/engine/external/zlib/gzread.c +++ b/src/engine/external/zlib/gzread.c @@ -5,25 +5,12 @@ #include "gzguts.h" -/* Local functions */ -local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); -local int gz_avail OF((gz_statep)); -local int gz_look OF((gz_statep)); -local int gz_decomp OF((gz_statep)); -local int gz_fetch OF((gz_statep)); -local int gz_skip OF((gz_statep, z_off64_t)); -local z_size_t gz_read OF((gz_statep, voidp, z_size_t)); - /* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from state->fd, and update state->eof, state->err, and state->msg as appropriate. This function needs to loop on read(), since read() is not guaranteed to read the number of bytes requested, depending on the type of descriptor. */ -local int gz_load(state, buf, len, have) - gz_statep state; - unsigned char *buf; - unsigned len; - unsigned *have; -{ +local int gz_load(gz_statep state, unsigned char *buf, unsigned len, + unsigned *have) { int ret; unsigned get, max = ((unsigned)-1 >> 2) + 1; @@ -53,9 +40,7 @@ local int gz_load(state, buf, len, have) If strm->avail_in != 0, then the current data is moved to the beginning of the input buffer, and then the remainder of the buffer is loaded with the available data from the input file. */ -local int gz_avail(state) - gz_statep state; -{ +local int gz_avail(gz_statep state) { unsigned got; z_streamp strm = &(state->strm); @@ -88,9 +73,7 @@ local int gz_avail(state) case, all further file reads will be directly to either the output buffer or a user buffer. If decompressing, the inflate state will be initialized. gz_look() will return 0 on success or -1 on failure. */ -local int gz_look(state) - gz_statep state; -{ +local int gz_look(gz_statep state) { z_streamp strm = &(state->strm); /* allocate read buffers and inflate memory */ @@ -170,9 +153,7 @@ local int gz_look(state) data. If the gzip stream completes, state->how is reset to LOOK to look for the next gzip stream or raw data, once state->x.have is depleted. Returns 0 on success, -1 on failure. */ -local int gz_decomp(state) - gz_statep state; -{ +local int gz_decomp(gz_statep state) { int ret = Z_OK; unsigned had; z_streamp strm = &(state->strm); @@ -224,9 +205,7 @@ local int gz_decomp(state) looked for to determine whether to copy or decompress. Returns -1 on error, otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the end of the input file has been reached and all data has been processed. */ -local int gz_fetch(state) - gz_statep state; -{ +local int gz_fetch(gz_statep state) { z_streamp strm = &(state->strm); do { @@ -254,10 +233,7 @@ local int gz_fetch(state) } /* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ -local int gz_skip(state, len) - gz_statep state; - z_off64_t len; -{ +local int gz_skip(gz_statep state, z_off64_t len) { unsigned n; /* skip over len bytes or reach end-of-file, whichever comes first */ @@ -289,11 +265,7 @@ local int gz_skip(state, len) input. Return the number of bytes read. If zero is returned, either the end of file was reached, or there was an error. state->err must be consulted in that case to determine which. */ -local z_size_t gz_read(state, buf, len) - gz_statep state; - voidp buf; - z_size_t len; -{ +local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { z_size_t got; unsigned n; @@ -370,11 +342,7 @@ local z_size_t gz_read(state, buf, len) } /* -- see zlib.h -- */ -int ZEXPORT gzread(file, buf, len) - gzFile file; - voidp buf; - unsigned len; -{ +int ZEXPORT gzread(gzFile file, voidp buf, unsigned len) { gz_statep state; /* get internal structure */ @@ -406,12 +374,7 @@ int ZEXPORT gzread(file, buf, len) } /* -- see zlib.h -- */ -z_size_t ZEXPORT gzfread(buf, size, nitems, file) - voidp buf; - z_size_t size; - z_size_t nitems; - gzFile file; -{ +z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file) { z_size_t len; gz_statep state; @@ -442,9 +405,7 @@ z_size_t ZEXPORT gzfread(buf, size, nitems, file) #else # undef gzgetc #endif -int ZEXPORT gzgetc(file) - gzFile file; -{ +int ZEXPORT gzgetc(gzFile file) { unsigned char buf[1]; gz_statep state; @@ -469,17 +430,12 @@ int ZEXPORT gzgetc(file) return gz_read(state, buf, 1) < 1 ? -1 : buf[0]; } -int ZEXPORT gzgetc_(file) -gzFile file; -{ +int ZEXPORT gzgetc_(gzFile file) { return gzgetc(file); } /* -- see zlib.h -- */ -int ZEXPORT gzungetc(c, file) - int c; - gzFile file; -{ +int ZEXPORT gzungetc(int c, gzFile file) { gz_statep state; /* get internal structure */ @@ -487,6 +443,10 @@ int ZEXPORT gzungetc(c, file) return -1; state = (gz_statep)file; + /* in case this was just opened, set up the input buffer */ + if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) + (void)gz_look(state); + /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) @@ -536,11 +496,7 @@ int ZEXPORT gzungetc(c, file) } /* -- see zlib.h -- */ -char * ZEXPORT gzgets(file, buf, len) - gzFile file; - char *buf; - int len; -{ +char * ZEXPORT gzgets(gzFile file, char *buf, int len) { unsigned left, n; char *str; unsigned char *eol; @@ -600,9 +556,7 @@ char * ZEXPORT gzgets(file, buf, len) } /* -- see zlib.h -- */ -int ZEXPORT gzdirect(file) - gzFile file; -{ +int ZEXPORT gzdirect(gzFile file) { gz_statep state; /* get internal structure */ @@ -620,9 +574,7 @@ int ZEXPORT gzdirect(file) } /* -- see zlib.h -- */ -int ZEXPORT gzclose_r(file) - gzFile file; -{ +int ZEXPORT gzclose_r(gzFile file) { int ret, err; gz_statep state; diff --git a/src/engine/external/zlib/gzwrite.c b/src/engine/external/zlib/gzwrite.c index eb8a0e5893f..435b4621b53 100644 --- a/src/engine/external/zlib/gzwrite.c +++ b/src/engine/external/zlib/gzwrite.c @@ -5,18 +5,10 @@ #include "gzguts.h" -/* Local functions */ -local int gz_init OF((gz_statep)); -local int gz_comp OF((gz_statep, int)); -local int gz_zero OF((gz_statep, z_off64_t)); -local z_size_t gz_write OF((gz_statep, voidpc, z_size_t)); - /* Initialize state for writing a gzip file. Mark initialization by setting state->size to non-zero. Return -1 on a memory allocation failure, or 0 on success. */ -local int gz_init(state) - gz_statep state; -{ +local int gz_init(gz_statep state) { int ret; z_streamp strm = &(state->strm); @@ -70,10 +62,7 @@ local int gz_init(state) deflate() flush value. If flush is Z_FINISH, then the deflate() state is reset to start a new gzip stream. If gz->direct is true, then simply write to the output file without compressing, and ignore flush. */ -local int gz_comp(state, flush) - gz_statep state; - int flush; -{ +local int gz_comp(gz_statep state, int flush) { int ret, writ; unsigned have, put, max = ((unsigned)-1 >> 2) + 1; z_streamp strm = &(state->strm); @@ -151,10 +140,7 @@ local int gz_comp(state, flush) /* Compress len zeros to output. Return -1 on a write error or memory allocation failure by gz_comp(), or 0 on success. */ -local int gz_zero(state, len) - gz_statep state; - z_off64_t len; -{ +local int gz_zero(gz_statep state, z_off64_t len) { int first; unsigned n; z_streamp strm = &(state->strm); @@ -184,11 +170,7 @@ local int gz_zero(state, len) /* Write len bytes from buf to file. Return the number of bytes written. If the returned value is less than len, then there was an error. */ -local z_size_t gz_write(state, buf, len) - gz_statep state; - voidpc buf; - z_size_t len; -{ +local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) { z_size_t put = len; /* if len is zero, avoid unnecessary operations */ @@ -252,11 +234,7 @@ local z_size_t gz_write(state, buf, len) } /* -- see zlib.h -- */ -int ZEXPORT gzwrite(file, buf, len) - gzFile file; - voidpc buf; - unsigned len; -{ +int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len) { gz_statep state; /* get internal structure */ @@ -280,12 +258,8 @@ int ZEXPORT gzwrite(file, buf, len) } /* -- see zlib.h -- */ -z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) - voidpc buf; - z_size_t size; - z_size_t nitems; - gzFile file; -{ +z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, z_size_t nitems, + gzFile file) { z_size_t len; gz_statep state; @@ -310,10 +284,7 @@ z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) } /* -- see zlib.h -- */ -int ZEXPORT gzputc(file, c) - gzFile file; - int c; -{ +int ZEXPORT gzputc(gzFile file, int c) { unsigned have; unsigned char buf[1]; gz_statep state; @@ -358,10 +329,7 @@ int ZEXPORT gzputc(file, c) } /* -- see zlib.h -- */ -int ZEXPORT gzputs(file, s) - gzFile file; - const char *s; -{ +int ZEXPORT gzputs(gzFile file, const char *s) { z_size_t len, put; gz_statep state; @@ -388,8 +356,7 @@ int ZEXPORT gzputs(file, s) #include /* -- see zlib.h -- */ -int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) -{ +int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { int len; unsigned left; char *next; @@ -460,8 +427,7 @@ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) return len; } -int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) -{ +int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) { va_list va; int ret; @@ -474,13 +440,10 @@ int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) #else /* !STDC && !Z_HAVE_STDARG_H */ /* -- see zlib.h -- */ -int ZEXPORTVA gzprintf(file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, - a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) - gzFile file; - const char *format; - int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, - a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; -{ +int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3, + int a4, int a5, int a6, int a7, int a8, int a9, int a10, + int a11, int a12, int a13, int a14, int a15, int a16, + int a17, int a18, int a19, int a20) { unsigned len, left; char *next; gz_statep state; @@ -562,10 +525,7 @@ int ZEXPORTVA gzprintf(file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, #endif /* -- see zlib.h -- */ -int ZEXPORT gzflush(file, flush) - gzFile file; - int flush; -{ +int ZEXPORT gzflush(gzFile file, int flush) { gz_statep state; /* get internal structure */ @@ -594,11 +554,7 @@ int ZEXPORT gzflush(file, flush) } /* -- see zlib.h -- */ -int ZEXPORT gzsetparams(file, level, strategy) - gzFile file; - int level; - int strategy; -{ +int ZEXPORT gzsetparams(gzFile file, int level, int strategy) { gz_statep state; z_streamp strm; @@ -609,7 +565,7 @@ int ZEXPORT gzsetparams(file, level, strategy) strm = &(state->strm); /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) + if (state->mode != GZ_WRITE || state->err != Z_OK || state->direct) return Z_STREAM_ERROR; /* if no change is requested, then do nothing */ @@ -636,9 +592,7 @@ int ZEXPORT gzsetparams(file, level, strategy) } /* -- see zlib.h -- */ -int ZEXPORT gzclose_w(file) - gzFile file; -{ +int ZEXPORT gzclose_w(gzFile file) { int ret = Z_OK; gz_statep state; diff --git a/src/engine/external/zlib/infback.c b/src/engine/external/zlib/infback.c index babeaf1806f..e7b25b307a3 100644 --- a/src/engine/external/zlib/infback.c +++ b/src/engine/external/zlib/infback.c @@ -15,9 +15,6 @@ #include "inflate.h" #include "inffast.h" -/* function prototypes */ -local void fixedtables OF((struct inflate_state FAR *state)); - /* strm provides memory allocation functions in zalloc and zfree, or Z_NULL to use the library memory allocation functions. @@ -25,13 +22,9 @@ local void fixedtables OF((struct inflate_state FAR *state)); windowBits is in the range 8..15, and window is a user-supplied window and output buffer that is 2**windowBits bytes. */ -int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) -z_streamp strm; -int windowBits; -unsigned char FAR *window; -const char *version; -int stream_size; -{ +int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, + unsigned char FAR *window, const char *version, + int stream_size) { struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || @@ -80,9 +73,7 @@ int stream_size; used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ -local void fixedtables(state) -struct inflate_state FAR *state; -{ +local void fixedtables(struct inflate_state FAR *state) { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; @@ -248,13 +239,8 @@ struct inflate_state FAR *state; inflateBack() can also return Z_STREAM_ERROR if the input parameters are not correct, i.e. strm is Z_NULL or the state was not initialized. */ -int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) -z_streamp strm; -in_func in; -void FAR *in_desc; -out_func out; -void FAR *out_desc; -{ +int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc) { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ @@ -632,9 +618,7 @@ void FAR *out_desc; return ret; } -int ZEXPORT inflateBackEnd(strm) -z_streamp strm; -{ +int ZEXPORT inflateBackEnd(z_streamp strm) { if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) return Z_STREAM_ERROR; ZFREE(strm, strm->state); diff --git a/src/engine/external/zlib/inffast.c b/src/engine/external/zlib/inffast.c index 1fec7f363fa..9354676e786 100644 --- a/src/engine/external/zlib/inffast.c +++ b/src/engine/external/zlib/inffast.c @@ -47,10 +47,7 @@ requires strm->avail_out >= 258 for each loop to avoid checking for output space. */ -void ZLIB_INTERNAL inflate_fast(strm, start) -z_streamp strm; -unsigned start; /* inflate()'s starting value for strm->avail_out */ -{ +void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) { struct inflate_state FAR *state; z_const unsigned char FAR *in; /* local strm->next_in */ z_const unsigned char FAR *last; /* have enough input while in < last */ diff --git a/src/engine/external/zlib/inffast.h b/src/engine/external/zlib/inffast.h index e5c1aa4ca8c..49c6d156c5c 100644 --- a/src/engine/external/zlib/inffast.h +++ b/src/engine/external/zlib/inffast.h @@ -8,4 +8,4 @@ subject to change. Applications should only use zlib.h. */ -void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); +void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start); diff --git a/src/engine/external/zlib/inflate.c b/src/engine/external/zlib/inflate.c index 8acbef44e99..94ecff015a9 100644 --- a/src/engine/external/zlib/inflate.c +++ b/src/engine/external/zlib/inflate.c @@ -91,20 +91,7 @@ # endif #endif -/* function prototypes */ -local int inflateStateCheck OF((z_streamp strm)); -local void fixedtables OF((struct inflate_state FAR *state)); -local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, - unsigned copy)); -#ifdef BUILDFIXED - void makefixed OF((void)); -#endif -local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, - unsigned len)); - -local int inflateStateCheck(strm) -z_streamp strm; -{ +local int inflateStateCheck(z_streamp strm) { struct inflate_state FAR *state; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) @@ -116,9 +103,7 @@ z_streamp strm; return 0; } -int ZEXPORT inflateResetKeep(strm) -z_streamp strm; -{ +int ZEXPORT inflateResetKeep(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -142,9 +127,7 @@ z_streamp strm; return Z_OK; } -int ZEXPORT inflateReset(strm) -z_streamp strm; -{ +int ZEXPORT inflateReset(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -155,10 +138,7 @@ z_streamp strm; return inflateResetKeep(strm); } -int ZEXPORT inflateReset2(strm, windowBits) -z_streamp strm; -int windowBits; -{ +int ZEXPORT inflateReset2(z_streamp strm, int windowBits) { int wrap; struct inflate_state FAR *state; @@ -195,12 +175,8 @@ int windowBits; return inflateReset(strm); } -int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) -z_streamp strm; -int windowBits; -const char *version; -int stream_size; -{ +int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, + const char *version, int stream_size) { int ret; struct inflate_state FAR *state; @@ -239,22 +215,17 @@ int stream_size; return ret; } -int ZEXPORT inflateInit_(strm, version, stream_size) -z_streamp strm; -const char *version; -int stream_size; -{ +int ZEXPORT inflateInit_(z_streamp strm, const char *version, + int stream_size) { return inflateInit2_(strm, DEF_WBITS, version, stream_size); } -int ZEXPORT inflatePrime(strm, bits, value) -z_streamp strm; -int bits; -int value; -{ +int ZEXPORT inflatePrime(z_streamp strm, int bits, int value) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + if (bits == 0) + return Z_OK; state = (struct inflate_state FAR *)strm->state; if (bits < 0) { state->hold = 0; @@ -278,9 +249,7 @@ int value; used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ -local void fixedtables(state) -struct inflate_state FAR *state; -{ +local void fixedtables(struct inflate_state FAR *state) { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; @@ -342,7 +311,7 @@ struct inflate_state FAR *state; a.out > inffixed.h */ -void makefixed() +void makefixed(void) { unsigned low, size; struct inflate_state state; @@ -396,11 +365,7 @@ void makefixed() output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ -local int updatewindow(strm, end, copy) -z_streamp strm; -const Bytef *end; -unsigned copy; -{ +local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy) { struct inflate_state FAR *state; unsigned dist; @@ -622,10 +587,7 @@ unsigned copy; will return Z_BUF_ERROR if it has not reached the end of the stream. */ -int ZEXPORT inflate(strm, flush) -z_streamp strm; -int flush; -{ +int ZEXPORT inflate(z_streamp strm, int flush) { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ @@ -1301,9 +1263,7 @@ int flush; return ret; } -int ZEXPORT inflateEnd(strm) -z_streamp strm; -{ +int ZEXPORT inflateEnd(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1315,11 +1275,8 @@ z_streamp strm; return Z_OK; } -int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) -z_streamp strm; -Bytef *dictionary; -uInt *dictLength; -{ +int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary, + uInt *dictLength) { struct inflate_state FAR *state; /* check state */ @@ -1338,11 +1295,8 @@ uInt *dictLength; return Z_OK; } -int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) -z_streamp strm; -const Bytef *dictionary; -uInt dictLength; -{ +int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary, + uInt dictLength) { struct inflate_state FAR *state; unsigned long dictid; int ret; @@ -1373,10 +1327,7 @@ uInt dictLength; return Z_OK; } -int ZEXPORT inflateGetHeader(strm, head) -z_streamp strm; -gz_headerp head; -{ +int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head) { struct inflate_state FAR *state; /* check state */ @@ -1401,11 +1352,8 @@ gz_headerp head; called again with more data and the *have state. *have is initialized to zero for the first call. */ -local unsigned syncsearch(have, buf, len) -unsigned FAR *have; -const unsigned char FAR *buf; -unsigned len; -{ +local unsigned syncsearch(unsigned FAR *have, const unsigned char FAR *buf, + unsigned len) { unsigned got; unsigned next; @@ -1424,9 +1372,7 @@ unsigned len; return next; } -int ZEXPORT inflateSync(strm) -z_streamp strm; -{ +int ZEXPORT inflateSync(z_streamp strm) { unsigned len; /* number of bytes to look at or looked at */ int flags; /* temporary to save header status */ unsigned long in, out; /* temporary to save total_in and total_out */ @@ -1441,7 +1387,7 @@ z_streamp strm; /* if first time, start search in bit buffer */ if (state->mode != SYNC) { state->mode = SYNC; - state->hold <<= state->bits & 7; + state->hold >>= state->bits & 7; state->bits -= state->bits & 7; len = 0; while (state->bits >= 8) { @@ -1482,9 +1428,7 @@ z_streamp strm; block. When decompressing, PPP checks that at the end of input packet, inflate is waiting for these length bytes. */ -int ZEXPORT inflateSyncPoint(strm) -z_streamp strm; -{ +int ZEXPORT inflateSyncPoint(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1492,10 +1436,7 @@ z_streamp strm; return state->mode == STORED && state->bits == 0; } -int ZEXPORT inflateCopy(dest, source) -z_streamp dest; -z_streamp source; -{ +int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) { struct inflate_state FAR *state; struct inflate_state FAR *copy; unsigned char FAR *window; @@ -1539,10 +1480,7 @@ z_streamp source; return Z_OK; } -int ZEXPORT inflateUndermine(strm, subvert) -z_streamp strm; -int subvert; -{ +int ZEXPORT inflateUndermine(z_streamp strm, int subvert) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1557,10 +1495,7 @@ int subvert; #endif } -int ZEXPORT inflateValidate(strm, check) -z_streamp strm; -int check; -{ +int ZEXPORT inflateValidate(z_streamp strm, int check) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1572,9 +1507,7 @@ int check; return Z_OK; } -long ZEXPORT inflateMark(strm) -z_streamp strm; -{ +long ZEXPORT inflateMark(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) @@ -1585,9 +1518,7 @@ z_streamp strm; (state->mode == MATCH ? state->was - state->length : 0)); } -unsigned long ZEXPORT inflateCodesUsed(strm) -z_streamp strm; -{ +unsigned long ZEXPORT inflateCodesUsed(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return (unsigned long)-1; state = (struct inflate_state FAR *)strm->state; diff --git a/src/engine/external/zlib/inftrees.c b/src/engine/external/zlib/inftrees.c index 57d2793bec9..98cfe164458 100644 --- a/src/engine/external/zlib/inftrees.c +++ b/src/engine/external/zlib/inftrees.c @@ -1,5 +1,5 @@ /* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2022 Mark Adler + * Copyright (C) 1995-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,7 +9,7 @@ #define MAXBITS 15 const char inflate_copyright[] = - " inflate 1.2.13 Copyright 1995-2022 Mark Adler "; + " inflate 1.3.1 Copyright 1995-2024 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -29,14 +29,9 @@ const char inflate_copyright[] = table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ -int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) -codetype type; -unsigned short FAR *lens; -unsigned codes; -code FAR * FAR *table; -unsigned FAR *bits; -unsigned short FAR *work; -{ +int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work) { unsigned len; /* a code's length in bits */ unsigned sym; /* index of code symbols */ unsigned min, max; /* minimum and maximum code lengths */ @@ -62,7 +57,7 @@ unsigned short FAR *work; 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 194, 65}; + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 203, 77}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, diff --git a/src/engine/external/zlib/inftrees.h b/src/engine/external/zlib/inftrees.h index f53665311c1..396f74b5da7 100644 --- a/src/engine/external/zlib/inftrees.h +++ b/src/engine/external/zlib/inftrees.h @@ -41,8 +41,8 @@ typedef struct { examples/enough.c found in the zlib distribution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes - returns returns 852, and "enough 30 6 15" for distance codes returns 592. - The initial root table size (9 or 6) is found in the fifth argument of the + returns 852, and "enough 30 6 15" for distance codes returns 592. The + initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in inflate.c and infback.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ @@ -57,6 +57,6 @@ typedef enum { DISTS } codetype; -int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, - unsigned codes, code FAR * FAR *table, - unsigned FAR *bits, unsigned short FAR *work)); +int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work); diff --git a/src/engine/external/zlib/trees.c b/src/engine/external/zlib/trees.c index 5f305c47221..6a523ef34e3 100644 --- a/src/engine/external/zlib/trees.c +++ b/src/engine/external/zlib/trees.c @@ -1,5 +1,5 @@ /* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2021 Jean-loup Gailly + * Copyright (C) 1995-2024 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -122,39 +122,116 @@ struct static_tree_desc_s { int max_length; /* max bit length for the codes */ }; -local const static_tree_desc static_l_desc = +#ifdef NO_INIT_GLOBAL_POINTERS +# define TCONST +#else +# define TCONST const +#endif + +local TCONST static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; -local const static_tree_desc static_d_desc = +local TCONST static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; -local const static_tree_desc static_bl_desc = +local TCONST static_tree_desc static_bl_desc = {(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== - * Local (static) routines in this file. + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 */ +local unsigned bi_reverse(unsigned code, int len) { + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} -local void tr_static_init OF((void)); -local void init_block OF((deflate_state *s)); -local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); -local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); -local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); -local void build_tree OF((deflate_state *s, tree_desc *desc)); -local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); -local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); -local int build_bl_tree OF((deflate_state *s)); -local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, - int blcodes)); -local void compress_block OF((deflate_state *s, const ct_data *ltree, - const ct_data *dtree)); -local int detect_data_type OF((deflate_state *s)); -local unsigned bi_reverse OF((unsigned code, int len)); -local void bi_windup OF((deflate_state *s)); -local void bi_flush OF((deflate_state *s)); +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(deflate_state *s) { + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(deflate_state *s) { + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef ZLIB_DEBUG + s->bits_sent = (s->bits_sent + 7) & ~7; +#endif +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes(ct_data *tree, int max_code, ushf *bl_count) { + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + unsigned code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + code = (code + bl_count[bits - 1]) << 1; + next_code[bits] = (ush)code; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = (ush)bi_reverse(next_code[len]++, len); + + Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len] - 1)); + } +} #ifdef GEN_TREES_H -local void gen_trees_header OF((void)); +local void gen_trees_header(void); #endif #ifndef ZLIB_DEBUG @@ -167,27 +244,12 @@ local void gen_trees_header OF((void)); send_bits(s, tree[c].Code, tree[c].Len); } #endif -/* =========================================================================== - * Output a short LSB first on the stream. - * IN assertion: there is enough room in pendingBuf. - */ -#define put_short(s, w) { \ - put_byte(s, (uch)((w) & 0xff)); \ - put_byte(s, (uch)((ush)(w) >> 8)); \ -} - /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ #ifdef ZLIB_DEBUG -local void send_bits OF((deflate_state *s, int value, int length)); - -local void send_bits(s, value, length) - deflate_state *s; - int value; /* value to send */ - int length; /* number of bits */ -{ +local void send_bits(deflate_state *s, int value, int length) { Tracevv((stderr," l %2d v %4x ", length, value)); Assert(length > 0 && length <= 15, "invalid length"); s->bits_sent += (ulg)length; @@ -229,8 +291,7 @@ local void send_bits(s, value, length) /* =========================================================================== * Initialize the various 'constant' tables. */ -local void tr_static_init() -{ +local void tr_static_init(void) { #if defined(GEN_TREES_H) || !defined(STDC) static int static_init_done = 0; int n; /* iterates over tree elements */ @@ -323,8 +384,7 @@ local void tr_static_init() ((i) == (last)? "\n};\n\n" : \ ((i) % (width) == (width) - 1 ? ",\n" : ", ")) -void gen_trees_header() -{ +void gen_trees_header(void) { FILE *header = fopen("trees.h", "w"); int i; @@ -373,12 +433,26 @@ void gen_trees_header() } #endif /* GEN_TREES_H */ +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(deflate_state *s) { + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->sym_next = s->matches = 0; +} + /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ -void ZLIB_INTERNAL _tr_init(s) - deflate_state *s; -{ +void ZLIB_INTERNAL _tr_init(deflate_state *s) { tr_static_init(); s->l_desc.dyn_tree = s->dyn_ltree; @@ -401,24 +475,6 @@ void ZLIB_INTERNAL _tr_init(s) init_block(s); } -/* =========================================================================== - * Initialize a new block. - */ -local void init_block(s) - deflate_state *s; -{ - int n; /* iterates over tree elements */ - - /* Initialize the trees. */ - for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; - for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; - for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; - - s->dyn_ltree[END_BLOCK].Freq = 1; - s->opt_len = s->static_len = 0L; - s->sym_next = s->matches = 0; -} - #define SMALLEST 1 /* Index within the heap array of least frequent node in the Huffman tree */ @@ -448,11 +504,7 @@ local void init_block(s) * when the heap property is re-established (each father smaller than its * two sons). */ -local void pqdownheap(s, tree, k) - deflate_state *s; - ct_data *tree; /* the tree to restore */ - int k; /* node to move down */ -{ +local void pqdownheap(deflate_state *s, ct_data *tree, int k) { int v = s->heap[k]; int j = k << 1; /* left son of k */ while (j <= s->heap_len) { @@ -483,10 +535,7 @@ local void pqdownheap(s, tree, k) * The length opt_len is updated; static_len is also updated if stree is * not null. */ -local void gen_bitlen(s, desc) - deflate_state *s; - tree_desc *desc; /* the tree descriptor */ -{ +local void gen_bitlen(deflate_state *s, tree_desc *desc) { ct_data *tree = desc->dyn_tree; int max_code = desc->max_code; const ct_data *stree = desc->stat_desc->static_tree; @@ -561,48 +610,9 @@ local void gen_bitlen(s, desc) } } -/* =========================================================================== - * Generate the codes for a given tree and bit counts (which need not be - * optimal). - * IN assertion: the array bl_count contains the bit length statistics for - * the given tree and the field len is set for all tree elements. - * OUT assertion: the field code is set for all tree elements of non - * zero code length. - */ -local void gen_codes(tree, max_code, bl_count) - ct_data *tree; /* the tree to decorate */ - int max_code; /* largest code with non zero frequency */ - ushf *bl_count; /* number of codes at each bit length */ -{ - ush next_code[MAX_BITS+1]; /* next code value for each bit length */ - unsigned code = 0; /* running code value */ - int bits; /* bit index */ - int n; /* code index */ - - /* The distribution counts are first used to generate the code values - * without bit reversal. - */ - for (bits = 1; bits <= MAX_BITS; bits++) { - code = (code + bl_count[bits - 1]) << 1; - next_code[bits] = (ush)code; - } - /* Check that the bit counts in bl_count are consistent. The last code - * must be all ones. - */ - Assert (code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, - "inconsistent bit counts"); - Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); - - for (n = 0; n <= max_code; n++) { - int len = tree[n].Len; - if (len == 0) continue; - /* Now reverse the bits */ - tree[n].Code = (ush)bi_reverse(next_code[len]++, len); - - Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", - n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len] - 1)); - } -} +#ifdef DUMP_BL_TREE +# include +#endif /* =========================================================================== * Construct one Huffman tree and assigns the code bit strings and lengths. @@ -612,10 +622,7 @@ local void gen_codes(tree, max_code, bl_count) * and corresponding code. The length opt_len is updated; static_len is * also updated if stree is not null. The field max_code is set. */ -local void build_tree(s, desc) - deflate_state *s; - tree_desc *desc; /* the tree descriptor */ -{ +local void build_tree(deflate_state *s, tree_desc *desc) { ct_data *tree = desc->dyn_tree; const ct_data *stree = desc->stat_desc->static_tree; int elems = desc->stat_desc->elems; @@ -700,11 +707,7 @@ local void build_tree(s, desc) * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ -local void scan_tree(s, tree, max_code) - deflate_state *s; - ct_data *tree; /* the tree to be scanned */ - int max_code; /* and its largest code of non zero frequency */ -{ +local void scan_tree(deflate_state *s, ct_data *tree, int max_code) { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ @@ -745,11 +748,7 @@ local void scan_tree(s, tree, max_code) * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ -local void send_tree(s, tree, max_code) - deflate_state *s; - ct_data *tree; /* the tree to be scanned */ - int max_code; /* and its largest code of non zero frequency */ -{ +local void send_tree(deflate_state *s, ct_data *tree, int max_code) { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ @@ -796,9 +795,7 @@ local void send_tree(s, tree, max_code) * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ -local int build_bl_tree(s) - deflate_state *s; -{ +local int build_bl_tree(deflate_state *s) { int max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ @@ -831,10 +828,8 @@ local int build_bl_tree(s) * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ -local void send_all_trees(s, lcodes, dcodes, blcodes) - deflate_state *s; - int lcodes, dcodes, blcodes; /* number of codes for each tree */ -{ +local void send_all_trees(deflate_state *s, int lcodes, int dcodes, + int blcodes) { int rank; /* index in bl_order */ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); @@ -860,12 +855,8 @@ local void send_all_trees(s, lcodes, dcodes, blcodes) /* =========================================================================== * Send a stored block */ -void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) - deflate_state *s; - charf *buf; /* input block */ - ulg stored_len; /* length of input block */ - int last; /* one if this is the last block for a file */ -{ +void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, + ulg stored_len, int last) { send_bits(s, (STORED_BLOCK<<1) + last, 3); /* send block type */ bi_windup(s); /* align on byte boundary */ put_short(s, (ush)stored_len); @@ -884,9 +875,7 @@ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) /* =========================================================================== * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) */ -void ZLIB_INTERNAL _tr_flush_bits(s) - deflate_state *s; -{ +void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s) { bi_flush(s); } @@ -894,9 +883,7 @@ void ZLIB_INTERNAL _tr_flush_bits(s) * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ -void ZLIB_INTERNAL _tr_align(s) - deflate_state *s; -{ +void ZLIB_INTERNAL _tr_align(deflate_state *s) { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); #ifdef ZLIB_DEBUG @@ -905,16 +892,108 @@ void ZLIB_INTERNAL _tr_align(s) bi_flush(s); } +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(deflate_state *s, const ct_data *ltree, + const ct_data *dtree) { + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned sx = 0; /* running index in symbol buffers */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->sym_next != 0) do { +#ifdef LIT_MEM + dist = s->d_buf[sx]; + lc = s->l_buf[sx++]; +#else + dist = s->sym_buf[sx++] & 0xff; + dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8; + lc = s->sym_buf[sx++]; +#endif + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code + LITERALS + 1, ltree); /* send length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= (unsigned)base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check for no overlay of pending_buf on needed symbols */ +#ifdef LIT_MEM + Assert(s->pending < 2 * (s->lit_bufsize + sx), "pendingBuf overflow"); +#else + Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); +#endif + + } while (sx < s->sym_next); + + send_code(s, END_BLOCK, ltree); +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "block list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(deflate_state *s) { + /* block_mask is the bit mask of block-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long block_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("block-listed") bytes. */ + for (n = 0; n <= 31; n++, block_mask >>= 1) + if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("allow-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "block-listed" or "allow-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and write out the encoded block. */ -void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) - deflate_state *s; - charf *buf; /* input block, or NULL if too old */ - ulg stored_len; /* length of input block */ - int last; /* one if this is the last block for a file */ -{ +void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, + ulg stored_len, int last) { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex = 0; /* index of last bit length code of non zero freq */ @@ -1011,14 +1090,15 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ -int ZLIB_INTERNAL _tr_tally(s, dist, lc) - deflate_state *s; - unsigned dist; /* distance of matched string */ - unsigned lc; /* match length - MIN_MATCH or unmatched char (dist==0) */ -{ +int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc) { +#ifdef LIT_MEM + s->d_buf[s->sym_next] = (ush)dist; + s->l_buf[s->sym_next++] = (uch)lc; +#else s->sym_buf[s->sym_next++] = (uch)dist; s->sym_buf[s->sym_next++] = (uch)(dist >> 8); s->sym_buf[s->sym_next++] = (uch)lc; +#endif if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; @@ -1035,147 +1115,3 @@ int ZLIB_INTERNAL _tr_tally(s, dist, lc) } return (s->sym_next == s->sym_end); } - -/* =========================================================================== - * Send the block data compressed using the given Huffman trees - */ -local void compress_block(s, ltree, dtree) - deflate_state *s; - const ct_data *ltree; /* literal tree */ - const ct_data *dtree; /* distance tree */ -{ - unsigned dist; /* distance of matched string */ - int lc; /* match length or unmatched char (if dist == 0) */ - unsigned sx = 0; /* running index in sym_buf */ - unsigned code; /* the code to send */ - int extra; /* number of extra bits to send */ - - if (s->sym_next != 0) do { - dist = s->sym_buf[sx++] & 0xff; - dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8; - lc = s->sym_buf[sx++]; - if (dist == 0) { - send_code(s, lc, ltree); /* send a literal byte */ - Tracecv(isgraph(lc), (stderr," '%c' ", lc)); - } else { - /* Here, lc is the match length - MIN_MATCH */ - code = _length_code[lc]; - send_code(s, code + LITERALS + 1, ltree); /* send length code */ - extra = extra_lbits[code]; - if (extra != 0) { - lc -= base_length[code]; - send_bits(s, lc, extra); /* send the extra length bits */ - } - dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - Assert (code < D_CODES, "bad d_code"); - - send_code(s, code, dtree); /* send the distance code */ - extra = extra_dbits[code]; - if (extra != 0) { - dist -= (unsigned)base_dist[code]; - send_bits(s, dist, extra); /* send the extra distance bits */ - } - } /* literal or match pair ? */ - - /* Check that the overlay between pending_buf and sym_buf is ok: */ - Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); - - } while (sx < s->sym_next); - - send_code(s, END_BLOCK, ltree); -} - -/* =========================================================================== - * Check if the data type is TEXT or BINARY, using the following algorithm: - * - TEXT if the two conditions below are satisfied: - * a) There are no non-portable control characters belonging to the - * "block list" (0..6, 14..25, 28..31). - * b) There is at least one printable character belonging to the - * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). - * - BINARY otherwise. - * - The following partially-portable control characters form a - * "gray list" that is ignored in this detection algorithm: - * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). - * IN assertion: the fields Freq of dyn_ltree are set. - */ -local int detect_data_type(s) - deflate_state *s; -{ - /* block_mask is the bit mask of block-listed bytes - * set bits 0..6, 14..25, and 28..31 - * 0xf3ffc07f = binary 11110011111111111100000001111111 - */ - unsigned long block_mask = 0xf3ffc07fUL; - int n; - - /* Check for non-textual ("block-listed") bytes. */ - for (n = 0; n <= 31; n++, block_mask >>= 1) - if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0)) - return Z_BINARY; - - /* Check for textual ("allow-listed") bytes. */ - if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 - || s->dyn_ltree[13].Freq != 0) - return Z_TEXT; - for (n = 32; n < LITERALS; n++) - if (s->dyn_ltree[n].Freq != 0) - return Z_TEXT; - - /* There are no "block-listed" or "allow-listed" bytes: - * this stream either is empty or has tolerated ("gray-listed") bytes only. - */ - return Z_BINARY; -} - -/* =========================================================================== - * Reverse the first len bits of a code, using straightforward code (a faster - * method would use a table) - * IN assertion: 1 <= len <= 15 - */ -local unsigned bi_reverse(code, len) - unsigned code; /* the value to invert */ - int len; /* its bit length */ -{ - register unsigned res = 0; - do { - res |= code & 1; - code >>= 1, res <<= 1; - } while (--len > 0); - return res >> 1; -} - -/* =========================================================================== - * Flush the bit buffer, keeping at most 7 bits in it. - */ -local void bi_flush(s) - deflate_state *s; -{ - if (s->bi_valid == 16) { - put_short(s, s->bi_buf); - s->bi_buf = 0; - s->bi_valid = 0; - } else if (s->bi_valid >= 8) { - put_byte(s, (Byte)s->bi_buf); - s->bi_buf >>= 8; - s->bi_valid -= 8; - } -} - -/* =========================================================================== - * Flush the bit buffer and align the output on a byte boundary - */ -local void bi_windup(s) - deflate_state *s; -{ - if (s->bi_valid > 8) { - put_short(s, s->bi_buf); - } else if (s->bi_valid > 0) { - put_byte(s, (Byte)s->bi_buf); - } - s->bi_buf = 0; - s->bi_valid = 0; -#ifdef ZLIB_DEBUG - s->bits_sent = (s->bits_sent + 7) & ~7; -#endif -} diff --git a/src/engine/external/zlib/uncompr.c b/src/engine/external/zlib/uncompr.c index f9532f46c1a..5e256663b45 100644 --- a/src/engine/external/zlib/uncompr.c +++ b/src/engine/external/zlib/uncompr.c @@ -24,12 +24,8 @@ Z_DATA_ERROR if the input data was corrupted, including if the input data is an incomplete zlib stream. */ -int ZEXPORT uncompress2(dest, destLen, source, sourceLen) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong *sourceLen; -{ +int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong *sourceLen) { z_stream stream; int err; const uInt max = (uInt)-1; @@ -83,11 +79,7 @@ int ZEXPORT uncompress2(dest, destLen, source, sourceLen) err; } -int ZEXPORT uncompress(dest, destLen, source, sourceLen) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong sourceLen; -{ +int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen) { return uncompress2(dest, destLen, source, &sourceLen); } diff --git a/src/engine/external/zlib/zconf.h b/src/engine/external/zlib/zconf.h index bf977d3e70a..62adc8d8431 100644 --- a/src/engine/external/zlib/zconf.h +++ b/src/engine/external/zlib/zconf.h @@ -1,5 +1,5 @@ /* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -241,7 +241,11 @@ #endif #ifdef Z_SOLO - typedef unsigned long z_size_t; +# ifdef _WIN64 + typedef unsigned long long z_size_t; +# else + typedef unsigned long z_size_t; +# endif #else # define z_longlong long long # if defined(NO_SIZE_T) @@ -296,14 +300,6 @@ # endif #endif -#ifndef Z_ARG /* function prototypes for stdarg */ -# if defined(STDC) || defined(Z_HAVE_STDARG_H) -# define Z_ARG(args) args -# else -# define Z_ARG(args) () -# endif -#endif - /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have @@ -520,7 +516,7 @@ typedef uLong FAR uLongf; #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else -# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# if defined(_WIN32) && !defined(__GNUC__) # define z_off64_t __int64 # else # define z_off64_t z_off_t diff --git a/src/engine/external/zlib/zlib.h b/src/engine/external/zlib/zlib.h index 953cb5012dc..8d4b932eaf6 100644 --- a/src/engine/external/zlib/zlib.h +++ b/src/engine/external/zlib/zlib.h @@ -1,7 +1,7 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.13, October 13th, 2022 + version 1.3.1, January 22nd, 2024 - Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -37,11 +37,11 @@ extern "C" { #endif -#define ZLIB_VERSION "1.2.13" -#define ZLIB_VERNUM 0x12d0 +#define ZLIB_VERSION "1.3.1" +#define ZLIB_VERNUM 0x1310 #define ZLIB_VER_MAJOR 1 -#define ZLIB_VER_MINOR 2 -#define ZLIB_VER_REVISION 13 +#define ZLIB_VER_MINOR 3 +#define ZLIB_VER_REVISION 1 #define ZLIB_VER_SUBREVISION 0 /* @@ -78,8 +78,8 @@ extern "C" { even in the case of corrupted input. */ -typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); -typedef void (*free_func) OF((voidpf opaque, voidpf address)); +typedef voidpf (*alloc_func)(voidpf opaque, uInt items, uInt size); +typedef void (*free_func)(voidpf opaque, voidpf address); struct internal_state; @@ -217,7 +217,7 @@ typedef gz_header FAR *gz_headerp; /* basic functions */ -ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +ZEXTERN const char * ZEXPORT zlibVersion(void); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check @@ -225,12 +225,12 @@ ZEXTERN const char * ZEXPORT zlibVersion OF((void)); */ /* -ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); +ZEXTERN int ZEXPORT deflateInit(z_streamp strm, int level); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default - allocation functions. + allocation functions. total_in, total_out, adler, and msg are initialized. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all @@ -247,7 +247,7 @@ ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); */ -ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +ZEXTERN int ZEXPORT deflate(z_streamp strm, int flush); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce @@ -320,8 +320,8 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that - avail_out is greater than six to avoid repeated flush markers due to - avail_out == 0 on return. + avail_out is greater than six when the flush marker begins, in order to avoid + repeated flush markers upon calling deflate() again when avail_out == 0. If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was @@ -360,7 +360,7 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); */ -ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +ZEXTERN int ZEXPORT deflateEnd(z_streamp strm); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending @@ -375,7 +375,7 @@ ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); /* -ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateInit(z_streamp strm); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by @@ -383,7 +383,8 @@ ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); read or consumed. The allocation of a sliding window will be deferred to the first call of inflate (if the decompression does not complete on the first call). If zalloc and zfree are set to Z_NULL, inflateInit updates - them to use default allocation functions. + them to use default allocation functions. total_in, total_out, adler, and + msg are initialized. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the @@ -397,7 +398,7 @@ ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); */ -ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +ZEXTERN int ZEXPORT inflate(z_streamp strm, int flush); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce @@ -517,7 +518,7 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); */ -ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateEnd(z_streamp strm); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending @@ -535,12 +536,12 @@ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); */ /* -ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy)); +ZEXTERN int ZEXPORT deflateInit2(z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy); This is another version of deflateInit with more compression options. The fields zalloc, zfree and opaque must be initialized before by the caller. @@ -607,9 +608,9 @@ ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, compression: this will be done by deflate(). */ -ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); +ZEXTERN int ZEXPORT deflateSetDictionary(z_streamp strm, + const Bytef *dictionary, + uInt dictLength); /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. When using the zlib format, this @@ -651,9 +652,9 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, not perform any compression: this will be done by deflate(). */ -ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, - Bytef *dictionary, - uInt *dictLength)); +ZEXTERN int ZEXPORT deflateGetDictionary(z_streamp strm, + Bytef *dictionary, + uInt *dictLength); /* Returns the sliding dictionary being maintained by deflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied @@ -673,8 +674,8 @@ ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, stream state is inconsistent. */ -ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, - z_streamp source)); +ZEXTERN int ZEXPORT deflateCopy(z_streamp dest, + z_streamp source); /* Sets the destination stream as a complete copy of the source stream. @@ -691,20 +692,20 @@ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, destination. */ -ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +ZEXTERN int ZEXPORT deflateReset(z_streamp strm); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate the internal compression state. The stream will leave the compression level and any other attributes that may have been - set unchanged. + set unchanged. total_in, total_out, adler, and msg are initialized. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ -ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, - int level, - int strategy)); +ZEXTERN int ZEXPORT deflateParams(z_streamp strm, + int level, + int strategy); /* Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2(). This can be @@ -729,7 +730,7 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, Then no more input data should be provided before the deflateParams() call. If this is done, the old level and strategy will be applied to the data compressed before deflateParams(), and the new level and strategy will be - applied to the the data compressed after deflateParams(). + applied to the data compressed after deflateParams(). deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if @@ -740,11 +741,11 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, retried with more output space. */ -ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, - int good_length, - int max_lazy, - int nice_length, - int max_chain)); +ZEXTERN int ZEXPORT deflateTune(z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain); /* Fine tune deflate's internal compression parameters. This should only be used by someone who understands the algorithm used by zlib's deflate for @@ -757,8 +758,8 @@ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ -ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, - uLong sourceLen)); +ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm, + uLong sourceLen); /* deflateBound() returns an upper bound on the compressed size after deflation of sourceLen bytes. It must be called after deflateInit() or @@ -772,9 +773,9 @@ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, than Z_FINISH or Z_NO_FLUSH are used. */ -ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, - unsigned *pending, - int *bits)); +ZEXTERN int ZEXPORT deflatePending(z_streamp strm, + unsigned *pending, + int *bits); /* deflatePending() returns the number of bytes and bits of output that have been generated, but not yet provided in the available output. The bytes not @@ -787,9 +788,9 @@ ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, stream state was inconsistent. */ -ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, - int bits, - int value)); +ZEXTERN int ZEXPORT deflatePrime(z_streamp strm, + int bits, + int value); /* deflatePrime() inserts bits in the deflate output stream. The intent is that this function is used to start off the deflate output with the bits @@ -804,8 +805,8 @@ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, source stream state was inconsistent. */ -ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, - gz_headerp head)); +ZEXTERN int ZEXPORT deflateSetHeader(z_streamp strm, + gz_headerp head); /* deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called @@ -821,16 +822,17 @@ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, gzip file" and give up. If deflateSetHeader is not used, the default gzip header has text false, - the time set to zero, and os set to 255, with no extra, name, or comment - fields. The gzip header is returned to the default state by deflateReset(). + the time set to zero, and os set to the current operating system, with no + extra, name, or comment fields. The gzip header is returned to the default + state by deflateReset(). deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* -ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, - int windowBits)); +ZEXTERN int ZEXPORT inflateInit2(z_streamp strm, + int windowBits); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized @@ -883,9 +885,9 @@ ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, deferred until inflate() is called. */ -ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); +ZEXTERN int ZEXPORT inflateSetDictionary(z_streamp strm, + const Bytef *dictionary, + uInt dictLength); /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, @@ -906,9 +908,9 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, inflate(). */ -ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, - Bytef *dictionary, - uInt *dictLength)); +ZEXTERN int ZEXPORT inflateGetDictionary(z_streamp strm, + Bytef *dictionary, + uInt *dictLength); /* Returns the sliding dictionary being maintained by inflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied @@ -921,7 +923,7 @@ ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, stream state is inconsistent. */ -ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateSync(z_streamp strm); /* Skips invalid compressed data until a possible full flush point (see above for the description of deflate with Z_FULL_FLUSH) can be found, or until all @@ -934,14 +936,14 @@ ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); inflateSync returns Z_OK if a possible full flush point has been found, Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. - In the success case, the application may save the current current value of - total_in which indicates where valid compressed data was found. In the - error case, the application may repeatedly call inflateSync, providing more - input each time, until success or end of the input data. + In the success case, the application may save the current value of total_in + which indicates where valid compressed data was found. In the error case, + the application may repeatedly call inflateSync, providing more input each + time, until success or end of the input data. */ -ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, - z_streamp source)); +ZEXTERN int ZEXPORT inflateCopy(z_streamp dest, + z_streamp source); /* Sets the destination stream as a complete copy of the source stream. @@ -956,18 +958,19 @@ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, destination. */ -ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateReset(z_streamp strm); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. + total_in, total_out, adler, and msg are initialized. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ -ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, - int windowBits)); +ZEXTERN int ZEXPORT inflateReset2(z_streamp strm, + int windowBits); /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted @@ -980,9 +983,9 @@ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, the windowBits parameter is invalid. */ -ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, - int bits, - int value)); +ZEXTERN int ZEXPORT inflatePrime(z_streamp strm, + int bits, + int value); /* This function inserts bits in the inflate input stream. The intent is that this function is used to start inflating at a bit position in the @@ -1001,7 +1004,7 @@ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, stream state was inconsistent. */ -ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +ZEXTERN long ZEXPORT inflateMark(z_streamp strm); /* This function returns two values, one in the lower 16 bits of the return value, and the other in the remaining upper bits, obtained by shifting the @@ -1029,8 +1032,8 @@ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); source stream state was inconsistent. */ -ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, - gz_headerp head)); +ZEXTERN int ZEXPORT inflateGetHeader(z_streamp strm, + gz_headerp head); /* inflateGetHeader() requests that gzip header information be stored in the provided gz_header structure. inflateGetHeader() may be called after @@ -1070,8 +1073,8 @@ ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, */ /* -ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, - unsigned char FAR *window)); +ZEXTERN int ZEXPORT inflateBackInit(z_streamp strm, int windowBits, + unsigned char FAR *window); Initialize the internal stream state for decompression using inflateBack() calls. The fields zalloc, zfree and opaque in strm must be initialized @@ -1091,13 +1094,13 @@ ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, the version of the header file. */ -typedef unsigned (*in_func) OF((void FAR *, - z_const unsigned char FAR * FAR *)); -typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); +typedef unsigned (*in_func)(void FAR *, + z_const unsigned char FAR * FAR *); +typedef int (*out_func)(void FAR *, unsigned char FAR *, unsigned); -ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, - in_func in, void FAR *in_desc, - out_func out, void FAR *out_desc)); +ZEXTERN int ZEXPORT inflateBack(z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc); /* inflateBack() does a raw inflate with a single call using a call-back interface for input and output. This is potentially more efficient than @@ -1165,7 +1168,7 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, cannot return Z_OK. */ -ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateBackEnd(z_streamp strm); /* All memory allocated by inflateBackInit() is freed. @@ -1173,7 +1176,7 @@ ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); state was inconsistent. */ -ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +ZEXTERN uLong ZEXPORT zlibCompileFlags(void); /* Return flags indicating compile-time options. Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: @@ -1226,8 +1229,8 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); you need special options. */ -ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); +ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size @@ -1241,9 +1244,9 @@ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, buffer. */ -ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen, - int level)); +ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level); /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte @@ -1257,15 +1260,15 @@ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, Z_STREAM_ERROR if the level parameter is invalid. */ -ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +ZEXTERN uLong ZEXPORT compressBound(uLong sourceLen); /* compressBound() returns an upper bound on the compressed size after compress() or compress2() on sourceLen bytes. It would be used before a compress() or compress2() call to allocate the destination buffer. */ -ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); +ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); /* Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size @@ -1282,8 +1285,8 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, buffer with the uncompressed data up to that point. */ -ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong *sourceLen)); +ZEXTERN int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen); /* Same as uncompress, except that sourceLen is a pointer, where the length of the source is *sourceLen. On return, *sourceLen is the number of @@ -1302,7 +1305,7 @@ ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ /* -ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode); Open the gzip (.gz) file at path for reading and decompressing, or compressing and writing. The mode parameter is as in fopen ("rb" or "wb") @@ -1339,7 +1342,7 @@ ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); file could not be opened. */ -ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode); /* Associate a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (if the file has @@ -1362,7 +1365,7 @@ ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); will not detect if fd is invalid (unless fd is -1). */ -ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +ZEXTERN int ZEXPORT gzbuffer(gzFile file, unsigned size); /* Set the internal buffer size used by this library's functions for file to size. The default buffer size is 8192 bytes. This function must be called @@ -1378,7 +1381,7 @@ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); too late. */ -ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +ZEXTERN int ZEXPORT gzsetparams(gzFile file, int level, int strategy); /* Dynamically update the compression level and strategy for file. See the description of deflateInit2 for the meaning of these parameters. Previously @@ -1389,7 +1392,7 @@ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); or Z_MEM_ERROR if there is a memory allocation error. */ -ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len); /* Read and decompress up to len uncompressed bytes from file into buf. If the input file is not in gzip format, gzread copies the given number of @@ -1419,8 +1422,8 @@ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); Z_STREAM_ERROR. */ -ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, - gzFile file)); +ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, + gzFile file); /* Read and decompress up to nitems items of size size from file into buf, otherwise operating as gzread() does. This duplicates the interface of @@ -1445,14 +1448,14 @@ ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, file, resetting and retrying on end-of-file, when size is not 1. */ -ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len)); +ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len); /* Compress and write the len uncompressed bytes at buf to file. gzwrite returns the number of uncompressed bytes written or 0 in case of error. */ -ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, - z_size_t nitems, gzFile file)); +ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, + z_size_t nitems, gzFile file); /* Compress and write nitems items of size size from buf to file, duplicating the interface of stdio's fwrite(), with size_t request and return types. If @@ -1465,7 +1468,7 @@ ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, is returned, and the error state is set to Z_STREAM_ERROR. */ -ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); /* Convert, format, compress, and write the arguments (...) to file under control of the string format, as in fprintf. gzprintf returns the number of @@ -1480,7 +1483,7 @@ ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); This can be determined using zlibCompileFlags(). */ -ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s); /* Compress and write the given null-terminated string s to file, excluding the terminating null character. @@ -1488,7 +1491,7 @@ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); gzputs returns the number of characters written, or -1 in case of error. */ -ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len); /* Read and decompress bytes from file into buf, until len-1 characters are read, or until a newline character is read and transferred to buf, or an @@ -1502,13 +1505,13 @@ ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); buf are indeterminate. */ -ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +ZEXTERN int ZEXPORT gzputc(gzFile file, int c); /* Compress and write c, converted to an unsigned char, into file. gzputc returns the value that was written, or -1 in case of error. */ -ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +ZEXTERN int ZEXPORT gzgetc(gzFile file); /* Read and decompress one byte from file. gzgetc returns this byte or -1 in case of end of file or error. This is implemented as a macro for speed. @@ -1517,7 +1520,7 @@ ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); points to has been clobbered or not. */ -ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +ZEXTERN int ZEXPORT gzungetc(int c, gzFile file); /* Push c back onto the stream for file to be read as the first character on the next read. At least one character of push-back is always allowed. @@ -1529,7 +1532,7 @@ ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); gzseek() or gzrewind(). */ -ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +ZEXTERN int ZEXPORT gzflush(gzFile file, int flush); /* Flush all pending output to file. The parameter flush is as in the deflate() function. The return value is the zlib error number (see function @@ -1545,8 +1548,8 @@ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); */ /* -ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, - z_off_t offset, int whence)); +ZEXTERN z_off_t ZEXPORT gzseek(gzFile file, + z_off_t offset, int whence); Set the starting position to offset relative to whence for the next gzread or gzwrite on file. The offset represents a number of bytes in the @@ -1564,7 +1567,7 @@ ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, would be before the current position. */ -ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +ZEXTERN int ZEXPORT gzrewind(gzFile file); /* Rewind file. This function is supported only for reading. @@ -1572,7 +1575,7 @@ ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); */ /* -ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +ZEXTERN z_off_t ZEXPORT gztell(gzFile file); Return the starting position for the next gzread or gzwrite on file. This position represents a number of bytes in the uncompressed data stream, @@ -1583,7 +1586,7 @@ ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); */ /* -ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); +ZEXTERN z_off_t ZEXPORT gzoffset(gzFile file); Return the current compressed (actual) read or write offset of file. This offset includes the count of bytes that precede the gzip stream, for example @@ -1592,7 +1595,7 @@ ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); be used for a progress indicator. On error, gzoffset() returns -1. */ -ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +ZEXTERN int ZEXPORT gzeof(gzFile file); /* Return true (1) if the end-of-file indicator for file has been set while reading, false (0) otherwise. Note that the end-of-file indicator is set @@ -1607,7 +1610,7 @@ ZEXTERN int ZEXPORT gzeof OF((gzFile file)); has grown since the previous end of file was detected. */ -ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +ZEXTERN int ZEXPORT gzdirect(gzFile file); /* Return true (1) if file is being copied directly while reading, or false (0) if file is a gzip stream being decompressed. @@ -1628,7 +1631,7 @@ ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); gzip file reading and decompression, which may not be desired.) */ -ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose(gzFile file); /* Flush all pending output for file, if necessary, close file and deallocate the (de)compression state. Note that once file is closed, you @@ -1641,8 +1644,8 @@ ZEXTERN int ZEXPORT gzclose OF((gzFile file)); last read ended in the middle of a gzip stream, or Z_OK on success. */ -ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); -ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_r(gzFile file); +ZEXTERN int ZEXPORT gzclose_w(gzFile file); /* Same as gzclose(), but gzclose_r() is only for use when reading, and gzclose_w() is only for use when writing or appending. The advantage to @@ -1653,7 +1656,7 @@ ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); zlib library. */ -ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum); /* Return the error message for the last error which occurred on file. errnum is set to zlib error number. If an error occurred in the file system @@ -1669,7 +1672,7 @@ ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); functions above that do not distinguish those cases in their return values. */ -ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +ZEXTERN void ZEXPORT gzclearerr(gzFile file); /* Clear the error and end-of-file flags for file. This is analogous to the clearerr() function in stdio. This is useful for continuing to read a gzip @@ -1686,7 +1689,7 @@ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); library. */ -ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +ZEXTERN uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. An Adler-32 value is in the range of a 32-bit @@ -1706,15 +1709,15 @@ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); if (adler != original_adler) error(); */ -ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, - z_size_t len)); +ZEXTERN uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, + z_size_t len); /* Same as adler32(), but with a size_t length. */ /* -ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, - z_off_t len2)); +ZEXTERN uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, + z_off_t len2); Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for @@ -1724,7 +1727,7 @@ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, negative, the result has no meaning or utility. */ -ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +ZEXTERN uLong ZEXPORT crc32(uLong crc, const Bytef *buf, uInt len); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer. @@ -1742,30 +1745,30 @@ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); if (crc != original_crc) error(); */ -ZEXTERN uLong ZEXPORT crc32_z OF((uLong crc, const Bytef *buf, - z_size_t len)); +ZEXTERN uLong ZEXPORT crc32_z(uLong crc, const Bytef *buf, + z_size_t len); /* Same as crc32(), but with a size_t length. */ /* -ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); +ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2); Combine two CRC-32 check values into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and - len2. + len2. len2 must be non-negative. */ /* -ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t len2)); +ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2); Return the operator corresponding to length len2, to be used with - crc32_combine_op(). + crc32_combine_op(). len2 must be non-negative. */ -ZEXTERN uLong ZEXPORT crc32_combine_op OF((uLong crc1, uLong crc2, uLong op)); +ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op); /* Give the same result as crc32_combine(), using op in place of len2. op is is generated from len2 by crc32_combine_gen(). This will be faster than @@ -1778,20 +1781,20 @@ ZEXTERN uLong ZEXPORT crc32_combine_op OF((uLong crc1, uLong crc2, uLong op)); /* deflateInit and inflateInit are macros to allow checking the zlib version * and the compiler's view of z_stream: */ -ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, - int windowBits, int memLevel, - int strategy, const char *version, - int stream_size)); -ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, - unsigned char FAR *window, - const char *version, - int stream_size)); +ZEXTERN int ZEXPORT deflateInit_(z_streamp strm, int level, + const char *version, int stream_size); +ZEXTERN int ZEXPORT inflateInit_(z_streamp strm, + const char *version, int stream_size); +ZEXTERN int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size); +ZEXTERN int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, + const char *version, int stream_size); +ZEXTERN int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size); #ifdef Z_PREFIX_SET # define z_deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) @@ -1836,7 +1839,7 @@ struct gzFile_s { unsigned char *next; z_off64_t pos; }; -ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +ZEXTERN int ZEXPORT gzgetc_(gzFile file); /* backward compatibility */ #ifdef Z_PREFIX_SET # undef z_gzgetc # define z_gzgetc(g) \ @@ -1853,13 +1856,13 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ * without large file support, _LFS64_LARGEFILE must also be true */ #ifdef Z_LARGE64 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); - ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off64_t)); + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); + ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); #endif #if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) @@ -1881,50 +1884,50 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ # define crc32_combine_gen crc32_combine_gen64 # endif # ifndef Z_LARGE64 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); - ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off_t)); + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int); + ZEXTERN z_off_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); # endif #else - ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); - ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); - ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); - ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t)); + ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *); + ZEXTERN z_off_t ZEXPORT gzseek(gzFile, z_off_t, int); + ZEXTERN z_off_t ZEXPORT gztell(gzFile); + ZEXTERN z_off_t ZEXPORT gzoffset(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); #endif #else /* Z_SOLO */ - ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t)); + ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); #endif /* !Z_SOLO */ /* undocumented functions */ -ZEXTERN const char * ZEXPORT zError OF((int)); -ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); -ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); -ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); -ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); -ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF((z_streamp)); -ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); -ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +ZEXTERN const char * ZEXPORT zError(int); +ZEXTERN int ZEXPORT inflateSyncPoint(z_streamp); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table(void); +ZEXTERN int ZEXPORT inflateUndermine(z_streamp, int); +ZEXTERN int ZEXPORT inflateValidate(z_streamp, int); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed(z_streamp); +ZEXTERN int ZEXPORT inflateResetKeep(z_streamp); +ZEXTERN int ZEXPORT deflateResetKeep(z_streamp); #if defined(_WIN32) && !defined(Z_SOLO) -ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, - const char *mode)); +ZEXTERN gzFile ZEXPORT gzopen_w(const wchar_t *path, + const char *mode); #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO -ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, - const char *format, - va_list va)); +ZEXTERN int ZEXPORTVA gzvprintf(gzFile file, + const char *format, + va_list va); # endif #endif diff --git a/src/engine/external/zlib/zutil.c b/src/engine/external/zlib/zutil.c index 9543ae825e3..b1c5d2d3c6d 100644 --- a/src/engine/external/zlib/zutil.c +++ b/src/engine/external/zlib/zutil.c @@ -24,13 +24,11 @@ z_const char * const z_errmsg[10] = { }; -const char * ZEXPORT zlibVersion() -{ +const char * ZEXPORT zlibVersion(void) { return ZLIB_VERSION; } -uLong ZEXPORT zlibCompileFlags() -{ +uLong ZEXPORT zlibCompileFlags(void) { uLong flags; flags = 0; @@ -121,9 +119,7 @@ uLong ZEXPORT zlibCompileFlags() # endif int ZLIB_INTERNAL z_verbose = verbose; -void ZLIB_INTERNAL z_error(m) - char *m; -{ +void ZLIB_INTERNAL z_error(char *m) { fprintf(stderr, "%s\n", m); exit(1); } @@ -132,9 +128,7 @@ void ZLIB_INTERNAL z_error(m) /* exported to allow conversion of error code to string for compress() and * uncompress() */ -const char * ZEXPORT zError(err) - int err; -{ +const char * ZEXPORT zError(int err) { return ERR_MSG(err); } @@ -148,22 +142,14 @@ const char * ZEXPORT zError(err) #ifndef HAVE_MEMCPY -void ZLIB_INTERNAL zmemcpy(dest, source, len) - Bytef* dest; - const Bytef* source; - uInt len; -{ +void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len) { if (len == 0) return; do { *dest++ = *source++; /* ??? to be unrolled */ } while (--len != 0); } -int ZLIB_INTERNAL zmemcmp(s1, s2, len) - const Bytef* s1; - const Bytef* s2; - uInt len; -{ +int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len) { uInt j; for (j = 0; j < len; j++) { @@ -172,10 +158,7 @@ int ZLIB_INTERNAL zmemcmp(s1, s2, len) return 0; } -void ZLIB_INTERNAL zmemzero(dest, len) - Bytef* dest; - uInt len; -{ +void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len) { if (len == 0) return; do { *dest++ = 0; /* ??? to be unrolled */ @@ -216,8 +199,7 @@ local ptr_table table[MAX_PTR]; * a protected system like OS/2. Use Microsoft C instead. */ -voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) -{ +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { voidpf buf; ulg bsize = (ulg)items*size; @@ -242,8 +224,7 @@ voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) return buf; } -void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) -{ +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { int n; (void)opaque; @@ -279,14 +260,12 @@ void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) # define _hfree hfree #endif -voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, uInt items, uInt size) -{ +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, uInt items, uInt size) { (void)opaque; return _halloc((long)items, size); } -void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) -{ +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { (void)opaque; _hfree(ptr); } @@ -299,25 +278,18 @@ void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) #ifndef MY_ZCALLOC /* Any system without a special alloc function */ #ifndef STDC -extern voidp malloc OF((uInt size)); -extern voidp calloc OF((uInt items, uInt size)); -extern void free OF((voidpf ptr)); +extern voidp malloc(uInt size); +extern voidp calloc(uInt items, uInt size); +extern void free(voidpf ptr); #endif -voidpf ZLIB_INTERNAL zcalloc(opaque, items, size) - voidpf opaque; - unsigned items; - unsigned size; -{ +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { (void)opaque; return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : (voidpf)calloc(items, size); } -void ZLIB_INTERNAL zcfree(opaque, ptr) - voidpf opaque; - voidpf ptr; -{ +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { (void)opaque; free(ptr); } diff --git a/src/engine/external/zlib/zutil.h b/src/engine/external/zlib/zutil.h index 0bc7f4ecd1c..48dd7febae6 100644 --- a/src/engine/external/zlib/zutil.h +++ b/src/engine/external/zlib/zutil.h @@ -1,5 +1,5 @@ /* zutil.h -- internal interface and configuration of the compression library - * Copyright (C) 1995-2022 Jean-loup Gailly, Mark Adler + * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -56,7 +56,7 @@ typedef unsigned long ulg; extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ -#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] +#define ERR_MSG(err) z_errmsg[(err) < -6 || (err) > 2 ? 9 : 2 - (err)] #define ERR_RETURN(strm,err) \ return (strm->msg = ERR_MSG(err), (err)) @@ -137,17 +137,8 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # endif #endif -#if defined(MACOS) || defined(TARGET_OS_MAC) +#if defined(MACOS) # define OS_CODE 7 -# ifndef Z_SOLO -# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os -# include /* for fdopen */ -# else -# ifndef fdopen -# define fdopen(fd,mode) NULL /* No fdopen() */ -# endif -# endif -# endif #endif #ifdef __acorn @@ -170,18 +161,6 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # define OS_CODE 19 #endif -#if defined(_BEOS_) || defined(RISCOS) -# define fdopen(fd,mode) NULL /* No fdopen() */ -#endif - -#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX -# if defined(_WIN32_WCE) -# define fdopen(fd,mode) NULL /* No fdopen() */ -# else -# define fdopen(fd,type) _fdopen(fd,type) -# endif -#endif - #if defined(__BORLANDC__) && !defined(MSDOS) #pragma warn -8004 #pragma warn -8008 @@ -191,9 +170,9 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* provide prototypes for these when building zlib without LFS */ #if !defined(_WIN32) && \ (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off_t)); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); #endif /* common defaults */ @@ -232,16 +211,16 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # define zmemzero(dest, len) memset(dest, 0, len) # endif #else - void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); - int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); - void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); + void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len); + int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len); + void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len); #endif /* Diagnostic functions */ #ifdef ZLIB_DEBUG # include extern int ZLIB_INTERNAL z_verbose; - extern void ZLIB_INTERNAL z_error OF((char *m)); + extern void ZLIB_INTERNAL z_error(char *m); # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) {if (z_verbose>=0) fprintf x ;} # define Tracev(x) {if (z_verbose>0) fprintf x ;} @@ -258,9 +237,9 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #endif #ifndef Z_SOLO - voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, - unsigned size)); - void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); + voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, + unsigned size); + void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr); #endif #define ZALLOC(strm, items, size) \ From 01ab03b238e590683fe45ab6ff0cedb01a59f593 Mon Sep 17 00:00:00 2001 From: Pioooooo Date: Mon, 9 Dec 2024 19:35:29 +0800 Subject: [PATCH 06/40] Disable auto rescue in death tile --- src/game/client/prediction/entities/character.cpp | 10 +++++++++- src/game/server/entities/character.cpp | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/game/client/prediction/entities/character.cpp b/src/game/client/prediction/entities/character.cpp index 69d41eddaff..b65924ccba3 100644 --- a/src/game/client/prediction/entities/character.cpp +++ b/src/game/client/prediction/entities/character.cpp @@ -989,12 +989,20 @@ void CCharacter::DDRaceTick() m_Core.m_IsInFreeze = false; for(const int Tile : aTiles) { - if(Tile == TILE_FREEZE || Tile == TILE_DFREEZE || Tile == TILE_LFREEZE) + if(Tile == TILE_FREEZE || Tile == TILE_DFREEZE || Tile == TILE_LFREEZE || Tile == TILE_DEATH) { m_Core.m_IsInFreeze = true; break; } } + m_Core.m_IsInFreeze |= (Collision()->GetCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH); } void CCharacter::DDRacePostCoreTick() diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 13205acb563..2dfb6628228 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -2132,12 +2132,20 @@ void CCharacter::DDRaceTick() m_Core.m_IsInFreeze = false; for(const int Tile : aTiles) { - if(Tile == TILE_FREEZE || Tile == TILE_DFREEZE || Tile == TILE_LFREEZE) + if(Tile == TILE_FREEZE || Tile == TILE_DFREEZE || Tile == TILE_LFREEZE || Tile == TILE_DEATH) { m_Core.m_IsInFreeze = true; break; } } + m_Core.m_IsInFreeze |= (Collision()->GetCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || + Collision()->GetFrontCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH); // look for save position for rescue feature // always update auto rescue From 6002f0f9e146115a0aec2e3095889a1c1c8ba5ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 9 Dec 2024 19:39:36 +0100 Subject: [PATCH 07/40] Fix stack-overflow in `demo_extract_chat` tool Don't store `CSnapshotDelta` object on the stack, as it's currently 524560 bytes large. See #9234, which almost doubled the size of `CSnapshotDelta` due to the type being changed from `int` to `uint64_t`. --- src/tools/demo_extract_chat.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tools/demo_extract_chat.cpp b/src/tools/demo_extract_chat.cpp index 323b139c9f3..0d555ab17c2 100644 --- a/src/tools/demo_extract_chat.cpp +++ b/src/tools/demo_extract_chat.cpp @@ -9,6 +9,8 @@ #include +#include + static const char *TOOL_NAME = "demo_extract_chat"; class CClientSnapshotHandler @@ -195,8 +197,8 @@ class CDemoPlayerMessageListener : public CDemoPlayer::IListener static int ExtractDemoChat(const char *pDemoFilePath, IStorage *pStorage) { - CSnapshotDelta DemoSnapshotDelta; - CDemoPlayer DemoPlayer(&DemoSnapshotDelta, false); + std::unique_ptr pDemoSnapshotDelta = std::make_unique(); + CDemoPlayer DemoPlayer(pDemoSnapshotDelta.get(), false); if(DemoPlayer.Load(pStorage, nullptr, pDemoFilePath, IStorage::TYPE_ALL_OR_ABSOLUTE) == -1) { From d5f2972e23918ed0fded939709b8213e8b1b8113 Mon Sep 17 00:00:00 2001 From: SollyBunny Date: Tue, 10 Dec 2024 02:06:09 +0000 Subject: [PATCH 08/40] Nameplate rework --- data/strong_weak.png | Bin 2043 -> 3256 bytes datasrc/content.py | 3 +- src/engine/shared/config_variables.h | 25 +- src/game/client/components/menus_settings.cpp | 161 ++---- src/game/client/components/nameplates.cpp | 506 +++++++++++------- src/game/client/components/nameplates.h | 124 ++++- 6 files changed, 474 insertions(+), 345 deletions(-) diff --git a/data/strong_weak.png b/data/strong_weak.png index fe79ea9e354d51378fb1125f3579862a66515915..26cc52924778c07de6a3d09e3a95efac81d6d7ad 100644 GIT binary patch literal 3256 zcmZ8jcRbtc7yc#)u6c`EE#aa=HG~>9qC%}2RjXD|YSb)>(nL|ZTw85z(HcdIqIQfT zR*g%IN)W3xB0-W0e!0It@ALVb_xyD}?>Xl=@AIB#mga^$e~A470057%k-invcQQ?v z1HwFS_(k9W02JqgL|PgnklUJOSW*>Z4R`qi)?Zy{KEJ2J*ZQQkGKQgw?)y zu0C~9@Qq%XkVBZ_mp!iBdYKEC|Bk(f#U}Sw#J#Ai=B*<;v+`lq{u zCdr*`?d=7SUL{VY4hHgW_VlnaR(gdJimMUPX9e{@`-eV^8e54KF8`9yXIgDG`>zYd;1*;-f_C2R$o?3p>WwWt= zI#H4CE{!}i=EL;tni8%0Z9sqZfqlP0HPIvl?MKz^{M^uVJ!2@zeNIvd+Qe0#*KqX6 zZ*EJV^Mu=J!x>Y>HiS-F*z_Iy%QZs0scG96ClU08<)zL)p2!C>2(vf#tP7*-dF0N1 z1;vl62U9bB8!RcM)#GZNSSdx9x0_+f;Z*9u!A_1j2I2ku{@Xo%ZYd@!5O-5UeWquE zj%>j&9cPfy?GOOq68P6ZKyJPW0Q@mvtbffqVs@(_>W1~q*}jf$E!POny4Hi65|^dx zb^4p@-5WV8llH3wUn+^*u^O=UeyLw)78?Bx_nW|XF|RC4zD$W<{=0+Vol1pIs+pyw zeBb`$2MMKq9*lu1hD$VJ<~51xBYBbYq9bZK9@}jpqw}Lj1$d3M@NJDXu=RiO7#19J ze;7>S1t2kQkl8C~9qbQrfq5uFSHce1XiXUrLBoI};7INw02)Wu9FgSUg@Q>sMZ3k9 z&Zq(2tgm@2i(de(ed2{=%>meM2uU9ZpBF}ob=s?^?m)?-+36?+3FK1~Ug&Zcf*r5GM?JNCJYv6O#&Te9ZPw#KbvWPHqoghOC6A;3eIG3o(44 zArSu!iGCdJ`FR)&l~YjAW+IpV1O#*|7H@297-(y2r!z*6A+DXtJQj5Aei%_q7Xr;Z z>F;NmnVG5k`-}#;Sk^S_L#jz>U6?u8Cv+LLtv8{cO-z*1>f^S6S6 zf^&LK4juW){*p4{=KBe=&G|1Bxlg8Bi3#r+E=1upmU!}3i(t0)>%rmSBt+=?w}eQ_ z>LW2hL67}K=a3Le(jj%HV^z%%w0D@gc%g%j_JjcZ8Et3#HnPUr&CSi-*m&Ug-rk;t zzI5uagnS(K^^ZEQ(J=REB=A8}q>y}=f`7%U_<~B)r?|E@HK}_}S37?xFE4*_mS@t@ z(eY$*exC6hAI-CfINFmrQIC0_fp3@50T;0ceD4yUZ45*`baPuwC@Cp17?JT-5eti= zZu>(YgmEyZ(1IlZO65qeYZ!5^iUC z4h|01oN8(AOackN)#^Qoa-aSSw{xaL?JTrs&vSRFcWm3}V@u1SOIX8F&qP4+sv2&);Whr!8@X>`c>e^_pAF2upX z;reHXEsY5dgz43=pVr!+=ptQ3yB?VsS66}o3We8=iaLBT*BHoG-_S6-f_U_MPsqJ4 zk=*iiM}YllQj!hO$sDS}(E?3sJe{wuwpQ8?OQlj9B{d&>NXM&?u`q@#OHaFz`e# z^@HX4G-Wv4(#|e1KRqBNYgu`@)EwFZ%M(pupIH`OArcu`l$4ZzqnDQjinOC1D5|KaNWL%@&R(@Z!q$uj1?`I%$1_L8QChK3eX(%@IGUR6<5RfRLN zC>irkI6OSOVo|6iYFqhFZdO*7n|lsdOy2q>*5zBrqBt1%BZqGL#w};(Bl!YzH7pq} zmh@XQe0%a)?^4pmOkYIgOV8W3Oub-MdE_C+i$-$~ni6iJaXXoE-y923j*cy%6YtCr zLeKOAOQ}r4qXq^Ba!S^I?f)JaA0Hp(lhoN9s$hG#@JkY)IT*=Gt`uo70wdA>{#(Yy zW8Nx}4Yrk!O2@nlKiPWpQeLspSm&+6DKym;cEBBoN2*uaspC#E$m|jwNRmnEeTxSV z4lJ%;zyAAde9>~x71rHr(^u@T)gM;!7tbta{VUv}v$~4cK{k5syc@;%j zTU|Zj!iXSDoT{kr`K?jqG11=MR)y^&NRK=1W>dY3MS5P>L z?tL!Ao{*SW1d6f0l(o9HmcG@l2olHp9490|(lBXct~|U;=#l@#E2f^J@e*2qwT+E7 zh(x(^Z>ppSfjP$8)wmormzAB@P*Dl}x!cI3S(6Zd4OnF{ikDllp8McoSfR&Y%9&>`w3YjLzs?IvX z^;Oy2b5_>Y8BRBE2J6bD8)T0R4-GML9I7PLY{US$D(g1eC=UCqHZ3-(Q~veXF*X9? z^1OH>Nj?)t#NkXDDVkE46>6k_H||cO-wb??mw^DH+3NPyhm{e7wSG1>|A>OoJ&@?M zJP~KJJ_Sa}E>k|4+G0pQyZrw!4G;LtWO$p)C^s)}nsShRd$Spsq~MryGG1!>K78|g zaI*RiO7$3Zx4gd5PTams$YVm`%=`eez8@ojpM0qpbkQ_$D=x{`*H_}#{zC;IJ|Uq! zWU=eXcx!8GgmjW0AeonIJw;FpTwr)fADIIu0zrTg-3!2^6hM6i0p`yifkbw*O@QRp zu%a{xU<_V9BZbzgIrw-lE$EVP|nvdGVSkvKPZ^-Ucn@y#yb0stD@kz~2>Yp9e6%|+A-pnjF z7VbcfU2rfbbSf7wMLXyYbE)+4@gc{?#`1|KjG+bYxSYd#;hgXGY;f;Y%9Sfuu3Wit<;vAu&|+pO%t-)%w7#y z0kemt)@Ww6j2y^+7qgYjaw!bX8=Nqg?*#C#AP4dTJeApVBtUySp6zSbuHC_XwoF3U9-qh6eOeFuPhXDZ zN5l&7d=kL(^77v4?Ckt05D1Kj;1C>=k&&^J`^`#e6!FWk{P!_?oF<1Y5#R*{1?3kn zUi^7%Y%HJ#uBfA<<45jy&(mb2JX_>g{)NmoFnfgr@Y2%KJ(n+EJ~uu-KBff@vAbe; z!c7czJnwMR#}iZ;WeP4_wjt^XHqmGB89+^ww`D|BEy^ zTuev-d=sf;ttu-kt9p8RI*mdrVqK#@H#hffuB_G4==9}i{)b4VtE;K0`RLlUYh7l+ z6AkHQXE zCzViJGr^xOl~6|yP^hB$`T1p5H~^6{h2e2`&6f$NBd7C|heDOzw{Ksi6%N?Fd-n$v zc0h(yI(<2vKbV#bR@Ue9eWnHG_xo?_Wxd#N=?@CSzf6h0=)o{QCU@o_Kc!Kq!lzH4 z{t6JtX?JyXb+os)pE`d0_?HzG75g#6g=6>`E;a4lyLUf@9gry#%%EcOx3#sMYG`N( z@@Hjb8RADTLSg>(GQo`MQ3_QUIcq@l_xE3=Fhs;Orxtu~Z||Sn@fcU%D~ZXBoX#K2 zcLytd>C&ZE6_6>zouK7_Lx&C>;EvCd#A8-Y=ci{VRB1fF0D`eF8^`|2y1*Z>j!pR< zkAqn7*RNmiqR7^NzdR&Uayox-ae|Tr`C9~o6O`}qOpN6Kuh;ttg&mMBk;km`_)pRt z;9o$KD^RganQxg$Pfm4l%GxzvxD%A`uUxs(p$3mQj8QRq0#qAi?RJY6}KA4420 zKoFm2NKHP;e^ymhRjoF^jk^NRLYax_2SpCAq}Gc$8%ZEbDM;lqb(h52b!c;vFt9U>S) zV8o*1U)tN-8_%3M^R>_C`y69kq)ml2?yyagn0YyxKMMZXv148ykg){-gJ+f+JpRY$ zWZ=#b43V$w*6(QkDEJd6PWaH8)w-_k?(W|$f=9Y@GfnP#l_^K_>%wDW!oa{lubMM7 z;IY3A<75nLkTFBr6oHk8CTGzSWsch@|37n)-w^z+UAx}L#)X?VZ~ki#-pmRXEOkQO zTih(ME%dpaOQ8($mz9;-$uCWYFFt8RyXX$NaV*Si8go67YWJv^U6Ue)FFx7JFGZ%{ z3kwU&wT3!;#*ZeKZ=1Pmqqa#2w#_PNPNw*=iG%S+ZP~JjUy35`IxTpZGS$grd3&q0xmz-pQ0ueXtZ#)XuoVR%?cNy(nEv9Zw*{1CcBJQMlA z2n!;(-syz<++y1I|K z<5$EucdUR!Eqjq^Q&HLqY|9p^2ecMr2uqUET9?Iae^6m*kd~9m+b(j^9MCG$1M(TRGeT2 z9^-1g?c0gq0K51%%o6@!w>DK!b8~Zp=mdUaA_AV_;o)1{A>)mWjrC;y;zP1y0K52& zTzp-A6E^MSSVUkG%Lx`=d`R{vzz%++7hjj(giSj=j&bDEe4#BZE#HYwu&CP?&nr6* zU?snqi?7RXz$~30Lm@f*3Ggz)33W}kw$9b-EaGG-JBZ}!gD zlB@;*#mhe@F215DC*Y;T2M-=RfcM+rUA1H{yo}jEUb6WULU00Bt{4eEtcI7h9)Ooj z{)Cd+tIClK!F%gdY&xgR6+xn8@F%1&X@a+3c_=~FuFbHV&hN^VD_5>uxpL*om8)5$ Z{{cDoLabel(&Label, Localize("Scoreboard"), HeadlineFontSize, TEXTALIGN_ML); + LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); + + ColorRGBA GreenDefault(0.78f, 1.0f, 0.8f, 1.0f); + static CButtonContainer s_AuthedColor, s_SameClanColor; + DoLine_ColorPicker(&s_AuthedColor, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &LeftView, Localize("Authed name color in scoreboard"), &g_Config.m_ClAuthedPlayerColor, GreenDefault, false); + DoLine_ColorPicker(&s_SameClanColor, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &LeftView, Localize("Same clan color in scoreboard"), &g_Config.m_ClSameClanColor, GreenDefault, false); + // ***** DDRace HUD ***** // RightView.HSplitTop(HeadlineHeight, &Label, &RightView); Ui()->DoLabel(&Label, Localize("DDRace HUD"), HeadlineFontSize, TEXTALIGN_ML); @@ -2739,33 +2750,52 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); // General name plate settings - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNameplates, Localize("Show name plates"), &g_Config.m_ClNameplates, &LeftView, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNamePlates, Localize("Show name plates"), &g_Config.m_ClNamePlates, &LeftView, LineSize); LeftView.HSplitTop(2 * LineSize, &Button, &LeftView); - Ui()->DoScrollbarOption(&g_Config.m_ClNameplatesSize, &g_Config.m_ClNameplatesSize, &Button, Localize("Name plates size"), 0, 100, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_MULTILINE); + Ui()->DoScrollbarOption(&g_Config.m_ClNamePlatesSize, &g_Config.m_ClNamePlatesSize, &Button, Localize("Name plates size"), -50, 100, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_MULTILINE); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNameplatesClan, Localize("Show clan above name plates"), &g_Config.m_ClNameplatesClan, &LeftView, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNamePlatesClan, Localize("Show clan above name plates"), &g_Config.m_ClNamePlatesClan, &LeftView, LineSize); LeftView.HSplitTop(2 * LineSize, &Button, &LeftView); - if(g_Config.m_ClNameplatesClan) + if(g_Config.m_ClNamePlatesClan) { - Ui()->DoScrollbarOption(&g_Config.m_ClNameplatesClanSize, &g_Config.m_ClNameplatesClanSize, &Button, Localize("Clan plates size"), 0, 100, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_MULTILINE); + Ui()->DoScrollbarOption(&g_Config.m_ClNamePlatesClanSize, &g_Config.m_ClNamePlatesClanSize, &Button, Localize("Clan plates size"), -50, 100, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_MULTILINE); } - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNameplatesTeamcolors, Localize("Use team colors for name plates"), &g_Config.m_ClNameplatesTeamcolors, &LeftView, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNameplatesFriendMark, Localize("Show friend mark (♥) in name plates"), &g_Config.m_ClNameplatesFriendMark, &LeftView, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNamePlatesTeamcolors, Localize("Use team colors for name plates"), &g_Config.m_ClNamePlatesTeamcolors, &LeftView, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNamePlatesFriendMark, Localize("Show friend mark (♥) in name plates"), &g_Config.m_ClNamePlatesFriendMark, &LeftView, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNamePlatesIds, Localize("Show client IDs in name plates"), &g_Config.m_ClNamePlatesIds, &LeftView, LineSize); + + // ***** Hook Strength ***** // + LeftView.HSplitTop(MarginBetweenViews, nullptr, &LeftView); + LeftView.HSplitTop(HeadlineHeight, &Label, &LeftView); + Ui()->DoLabel(&Label, Localize("Hook Strength"), HeadlineFontSize, TEXTALIGN_ML); + LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); LeftView.HSplitTop(LineSize, &Button, &LeftView); - if(DoButton_CheckBox(&g_Config.m_ClNameplatesStrong, Localize("Show hook strength icon indicator"), g_Config.m_ClNameplatesStrong, &Button)) + if(DoButton_CheckBox(&g_Config.m_ClNamePlatesStrong, Localize("Show hook strength icon indicator"), g_Config.m_ClNamePlatesStrong, &Button)) { - g_Config.m_ClNameplatesStrong = g_Config.m_ClNameplatesStrong ? 0 : 1; + g_Config.m_ClNamePlatesStrong = g_Config.m_ClNamePlatesStrong ? 0 : 1; } LeftView.HSplitTop(LineSize, &Button, &LeftView); - if(g_Config.m_ClNameplatesStrong) + if(g_Config.m_ClNamePlatesStrong) + { + static int s_NamePlatesStrong = 0; + if(DoButton_CheckBox(&s_NamePlatesStrong, Localize("Show hook strength number indicator"), g_Config.m_ClNamePlatesStrong == 2, &Button)) + g_Config.m_ClNamePlatesStrong = g_Config.m_ClNamePlatesStrong != 2 ? 2 : 1; + } + + LeftView.HSplitTop(2 * LineSize, &Button, &LeftView); + if(g_Config.m_ClNamePlatesStrong) { - static int s_NameplatesStrong = 0; - if(DoButton_CheckBox(&s_NameplatesStrong, Localize("Show hook strength number indicator"), g_Config.m_ClNameplatesStrong == 2, &Button)) - g_Config.m_ClNameplatesStrong = g_Config.m_ClNameplatesStrong != 2 ? 2 : 1; + Ui()->DoScrollbarOption(&g_Config.m_ClNamePlatesStrongSize, &g_Config.m_ClNamePlatesStrongSize, &Button, Localize("Size of hook strength icon and number indicator"), -50, 100, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_MULTILINE); } + // ***** Key Presses ***** // + LeftView.HSplitTop(MarginBetweenViews, nullptr, &LeftView); + LeftView.HSplitTop(HeadlineHeight, &Label, &LeftView); + Ui()->DoLabel(&Label, Localize("Key Presses"), HeadlineFontSize, TEXTALIGN_ML); + LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); + LeftView.HSplitTop(LineSize, &Button, &LeftView); if(DoButton_CheckBox(&g_Config.m_ClShowDirection, Localize("Show other players' key presses"), g_Config.m_ClShowDirection >= 1 && g_Config.m_ClShowDirection != 3, &Button)) { @@ -2779,111 +2809,26 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) g_Config.m_ClShowDirection = g_Config.m_ClShowDirection ^ 3; } - ColorRGBA GreenDefault(0.78f, 1.0f, 0.8f, 1.0f); - static CButtonContainer s_AuthedColor, s_SameClanColor; - DoLine_ColorPicker(&s_AuthedColor, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &LeftView, Localize("Authed name color in scoreboard"), &g_Config.m_ClAuthedPlayerColor, GreenDefault, false); - DoLine_ColorPicker(&s_SameClanColor, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &LeftView, Localize("Same clan color in scoreboard"), &g_Config.m_ClSameClanColor, GreenDefault, false); + LeftView.HSplitTop(2 * LineSize, &Button, &LeftView); + if(g_Config.m_ClShowDirection > 0) + { + Ui()->DoScrollbarOption(&g_Config.m_ClDirectionSize, &g_Config.m_ClDirectionSize, &Button, Localize("Size of key press icons"), -50, 100, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_MULTILINE); + } // ***** Name Plate Preview ***** // RightView.HSplitTop(HeadlineHeight, &Label, &RightView); Ui()->DoLabel(&Label, Localize("Preview"), HeadlineFontSize, TEXTALIGN_ML); - RightView.HSplitTop(2 * MarginSmall, nullptr, &RightView); + RightView.HSplitTop(2.0f * MarginSmall, nullptr, &RightView); CTeeRenderInfo TeeRenderInfo; TeeRenderInfo.Apply(m_pClient->m_Skins.Find(g_Config.m_ClPlayerSkin)); TeeRenderInfo.ApplyColors(g_Config.m_ClPlayerUseCustomColor, g_Config.m_ClPlayerColorBody, g_Config.m_ClPlayerColorFeet); TeeRenderInfo.m_Size = 64.0f; - const vec2 TeeRenderPos = vec2(RightView.x + RightView.w / 2, RightView.y + RightView.h / 2); - RenderTools()->RenderTee(CAnimState::GetIdle(), &TeeRenderInfo, 0, vec2(1.0f, 0.0f), TeeRenderPos); - - const float FontSize = 18.0f + 20.0f * g_Config.m_ClNameplatesSize / 100.0f; - const float FontSizeClan = 18.0f + 20.0f * g_Config.m_ClNameplatesClanSize / 100.0f; - const ColorRGBA Rgb = g_Config.m_ClNameplatesTeamcolors ? m_pClient->GetDDTeamColor(13, 0.75f) : TextRender()->DefaultTextColor(); - float YOffset = TeeRenderPos.y - 38; - TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE); - - if(g_Config.m_ClShowDirection) - { - const float ShowDirectionImgSize = 22.0f; - YOffset -= ShowDirectionImgSize; - const vec2 ShowDirectionPos = vec2(TeeRenderPos.x - 11.0f, YOffset); - TextRender()->TextColor(TextRender()->DefaultTextColor()); - - // Left - Graphics()->TextureSet(g_pData->m_aImages[IMAGE_ARROW].m_Id); - Graphics()->QuadsSetRotation(pi); - Graphics()->RenderQuadContainerAsSprite(m_DirectionQuadContainerIndex, 0, ShowDirectionPos.x - 30.f, ShowDirectionPos.y); - - // Right - Graphics()->TextureSet(g_pData->m_aImages[IMAGE_ARROW].m_Id); - Graphics()->QuadsSetRotation(0); - Graphics()->RenderQuadContainerAsSprite(m_DirectionQuadContainerIndex, 0, ShowDirectionPos.x + 30.f, ShowDirectionPos.y); - - // Jump - Graphics()->TextureSet(g_pData->m_aImages[IMAGE_ARROW].m_Id); - Graphics()->QuadsSetRotation(pi * 3 / 2); - Graphics()->RenderQuadContainerAsSprite(m_DirectionQuadContainerIndex, 0, ShowDirectionPos.x, ShowDirectionPos.y); - - Graphics()->QuadsSetRotation(0); - } - - if(g_Config.m_ClNameplates) - { - YOffset -= FontSize; - TextRender()->TextColor(Rgb); - TextRender()->TextOutlineColor(ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f)); - TextRender()->Text(TeeRenderPos.x - TextRender()->TextWidth(FontSize, g_Config.m_PlayerName) / 2.0f, YOffset, FontSize, g_Config.m_PlayerName); - if(g_Config.m_ClNameplatesClan) - { - YOffset -= FontSizeClan; - TextRender()->Text(TeeRenderPos.x - TextRender()->TextWidth(FontSizeClan, g_Config.m_PlayerClan) / 2.0f, YOffset, FontSizeClan, g_Config.m_PlayerClan); - } - TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor()); - - if(g_Config.m_ClNameplatesFriendMark) - { - YOffset -= FontSize; - TextRender()->TextColor(ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)); - TextRender()->Text(TeeRenderPos.x - TextRender()->TextWidth(FontSize, "♥") / 2.0f, YOffset, FontSize, "♥"); - } + const vec2 Position = RightView.Center(); + RenderTools()->RenderTee(CAnimState::GetIdle(), &TeeRenderInfo, 0, vec2(1.0f, 0.0f), Position); - if(g_Config.m_ClNameplatesIds) - { - YOffset -= FontSize; - TextRender()->TextColor(Rgb); - TextRender()->Text(TeeRenderPos.x - TextRender()->TextWidth(FontSize, "0") / 2.0f, YOffset, FontSize, "0"); - } - - if(g_Config.m_ClNameplatesStrong) - { - Graphics()->TextureSet(g_pData->m_aImages[IMAGE_STRONGWEAK].m_Id); - Graphics()->QuadsBegin(); - const ColorRGBA StrongStatusColor = color_cast(ColorHSLA(6401973)); - const int StrongSpriteId = SPRITE_HOOK_STRONG; - - Graphics()->SetColor(StrongStatusColor); - float ScaleX, ScaleY; - RenderTools()->SelectSprite(StrongSpriteId); - RenderTools()->GetSpriteScale(StrongSpriteId, ScaleX, ScaleY); - TextRender()->TextColor(StrongStatusColor); - - const float StrongImgSize = 40.0f; - YOffset -= StrongImgSize * ScaleY; - RenderTools()->DrawSprite(TeeRenderPos.x, YOffset + (StrongImgSize / 2.0f) * ScaleY, StrongImgSize); - Graphics()->QuadsEnd(); - - if(g_Config.m_ClNameplatesStrong == 2) - { - YOffset -= FontSize; - TextRender()->Text(TeeRenderPos.x - TextRender()->TextWidth(FontSize, "0") / 2.0f, YOffset, FontSize, "0"); - } - } - } - - TextRender()->TextColor(TextRender()->DefaultTextColor()); - TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor()); - TextRender()->SetRenderFlags(0); + GameClient()->m_NamePlates.RenderNamePlatePreview(Position); } else if(s_CurTab == APPEARANCE_TAB_HOOK_COLLISION) { @@ -3269,7 +3214,7 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView) Ui()->DoScrollbarOption(&g_Config.m_ClShowOthersAlpha, &g_Config.m_ClShowOthersAlpha, &Button, Localize("Opacity"), 0, 100, &CUi::ms_LinearScrollbarScale, 0u, "%"); - GameClient()->m_Tooltips.DoToolTip(&g_Config.m_ClShowOthersAlpha, &Button, Localize("Adjust the opacity of entities belonging to other teams, such as tees and nameplates")); + GameClient()->m_Tooltips.DoToolTip(&g_Config.m_ClShowOthersAlpha, &Button, Localize("Adjust the opacity of entities belonging to other teams, such as tees and name plates")); Left.HSplitTop(20.0f, &Button, &Left); static int s_ShowOwnTeamId = 0; diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index 59366afe516..138c5d67c7c 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -14,197 +14,300 @@ #include "controls.h" #include "nameplates.h" -void CNamePlates::RenderNameplate(vec2 Position, const CNetObj_PlayerInfo *pPlayerInfo, float Alpha, bool ForceAlpha) +void CNamePlate::CNamePlateName::Update(CNamePlates &This, int Id, const char *pName, bool FriendMark, float FontSize) { - SPlayerNamePlate &NamePlate = m_aNamePlates[pPlayerInfo->m_ClientId]; - const auto &ClientData = m_pClient->m_aClients[pPlayerInfo->m_ClientId]; - const bool OtherTeam = m_pClient->IsOtherTeam(pPlayerInfo->m_ClientId); - - const float FontSize = 18.0f + 20.0f * g_Config.m_ClNameplatesSize / 100.0f; - const float FontSizeClan = 18.0f + 20.0f * g_Config.m_ClNameplatesClanSize / 100.0f; + if(Id == m_Id && + str_comp(m_aName, pName) == 0 && + m_FriendMark == FriendMark && m_FontSize == FontSize) + return; + m_Id = Id; + str_copy(m_aName, pName); + m_FriendMark = FriendMark; + m_FontSize = FontSize; - TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE); - float YOffset = Position.y - 38; - ColorRGBA rgb = ColorRGBA(1.0f, 1.0f, 1.0f); + // create namePlates at standard zoom + float ScreenX0, ScreenY0, ScreenX1, ScreenY1; + This.Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); + This.RenderTools()->MapScreenToInterface(This.m_pClient->m_Camera.m_Center.x, This.m_pClient->m_Camera.m_Center.y); - // render players' key presses - int ShowDirection = g_Config.m_ClShowDirection; -#if defined(CONF_VIDEORECORDER) - if(IVideo::Current()) - ShowDirection = g_Config.m_ClVideoShowDirection; -#endif - if((ShowDirection && ShowDirection != 3 && !pPlayerInfo->m_Local) || (ShowDirection >= 2 && pPlayerInfo->m_Local) || (ShowDirection == 3 && Client()->DummyConnected() && Client()->State() != IClient::STATE_DEMOPLAYBACK && pPlayerInfo->m_ClientId == m_pClient->m_aLocalIds[!g_Config.m_ClDummy])) + CTextCursor Cursor; + This.TextRender()->SetCursor(&Cursor, 0.0f, 0.0f, FontSize, TEXTFLAG_RENDER); + This.TextRender()->DeleteTextContainer(m_TextContainerIndex); + if(m_FriendMark) { - bool DirLeft; - bool DirRight; - bool Jump; - if(Client()->DummyConnected() && Client()->State() != IClient::STATE_DEMOPLAYBACK && pPlayerInfo->m_ClientId == m_pClient->m_aLocalIds[!g_Config.m_ClDummy]) - { - const auto &InputData = m_pClient->m_Controls.m_aInputData[!g_Config.m_ClDummy]; - DirLeft = InputData.m_Direction == -1; - DirRight = InputData.m_Direction == 1; - Jump = InputData.m_Jump == 1; - } - else if(pPlayerInfo->m_Local && Client()->State() != IClient::STATE_DEMOPLAYBACK) - { - const auto &InputData = m_pClient->m_Controls.m_aInputData[g_Config.m_ClDummy]; - DirLeft = InputData.m_Direction == -1; - DirRight = InputData.m_Direction == 1; - Jump = InputData.m_Jump == 1; - } - else - { - const auto &Character = m_pClient->m_Snap.m_aCharacters[pPlayerInfo->m_ClientId]; - DirLeft = Character.m_Cur.m_Direction == -1; - DirRight = Character.m_Cur.m_Direction == 1; - Jump = Character.m_Cur.m_Jumped & 1; - } + This.TextRender()->TextColor(ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)); + This.TextRender()->CreateOrAppendTextContainer(m_TextContainerIndex, &Cursor, "♥"); + } + This.TextRender()->TextColor(ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f)); + char aBuf[16] = ""; + if(Id >= 0 && pName[0] != '\0' && FriendMark) + str_format(aBuf, sizeof(aBuf), " %d: ", Id); + else if(Id >= 0 && pName[0] != '\0') + str_format(aBuf, sizeof(aBuf), "%d: ", Id); + else if(Id >= 0 && FriendMark) + str_format(aBuf, sizeof(aBuf), " %d", Id); + else if(FriendMark && pName[0] != '\0') + str_copy(aBuf, " ", sizeof(aBuf)); + if(aBuf[0] != '\0') + This.TextRender()->CreateOrAppendTextContainer(m_TextContainerIndex, &Cursor, aBuf); + if(pName[0] != '\0') + This.TextRender()->CreateOrAppendTextContainer(m_TextContainerIndex, &Cursor, pName); + + This.Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); +} - if(OtherTeam && !ForceAlpha) - Graphics()->SetColor(1.0f, 1.0f, 1.0f, g_Config.m_ClShowOthersAlpha / 100.0f); - else - Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); +void CNamePlate::CNamePlateClan::Update(CNamePlates &This, const char *pClan, float FontSize) +{ + if(str_comp(m_aClan, pClan) == 0 && + m_FontSize == FontSize) + return; + str_copy(m_aClan, pClan); + m_FontSize = FontSize; + + // create namePlates at standard zoom + float ScreenX0, ScreenY0, ScreenX1, ScreenY1; + This.Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); + This.RenderTools()->MapScreenToInterface(This.m_pClient->m_Camera.m_Center.x, This.m_pClient->m_Camera.m_Center.y); + + CTextCursor Cursor; + This.TextRender()->SetCursor(&Cursor, 0.0f, 0.0f, FontSize, TEXTFLAG_RENDER); + This.TextRender()->RecreateTextContainer(m_TextContainerIndex, &Cursor, m_aClan); + + This.Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); +} + +void CNamePlate::CNamePlateHookWeakStrongId::Update(CNamePlates &This, int Id, float FontSize) +{ + if(Id == m_Id && m_FontSize == FontSize) + return; + m_Id = Id; + m_FontSize = FontSize; + + // create namePlates at standard zoom + float ScreenX0, ScreenY0, ScreenX1, ScreenY1; + This.Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); + This.RenderTools()->MapScreenToInterface(This.m_pClient->m_Camera.m_Center.x, This.m_pClient->m_Camera.m_Center.y); + + char aBuf[8]; + str_format(aBuf, sizeof(aBuf), "%d", m_Id); + + CTextCursor Cursor; + This.TextRender()->SetCursor(&Cursor, 0.0f, 0.0f, FontSize, TEXTFLAG_RENDER); + This.TextRender()->RecreateTextContainer(m_TextContainerIndex, &Cursor, aBuf); + + This.Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); +} - const float ShowDirectionImgSize = 22.0f; - YOffset -= ShowDirectionImgSize; - const vec2 ShowDirectionPos = vec2(Position.x - 11.0f, YOffset); - if(DirLeft) +void CNamePlates::RenderNamePlate(CNamePlate &NamePlate, const CRenderNamePlateData &Data) +{ + ColorRGBA OutlineColor = Data.m_OutlineColor.WithAlpha(Data.m_Alpha / 2.0f); + ColorRGBA Color = Data.m_Color.WithAlpha(Data.m_Alpha); + + float YOffset = Data.m_Position.y - 38.0f; + + // Render directions + TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE); + if(Data.m_ShowDirection) + { + Graphics()->SetColor(1.0f, 1.0f, 1.0f, Data.m_Alpha); + YOffset -= Data.m_FontSizeDirection; + const vec2 ShowDirectionPos = vec2(Data.m_Position.x - Data.m_FontSizeDirection / 2.0f, YOffset + Data.m_FontSizeDirection / 2.0f); + if(Data.m_DirLeft) { Graphics()->TextureSet(g_pData->m_aImages[IMAGE_ARROW].m_Id); Graphics()->QuadsSetRotation(pi); - Graphics()->RenderQuadContainerAsSprite(m_DirectionQuadContainerIndex, 0, ShowDirectionPos.x - 30.f, ShowDirectionPos.y); + Graphics()->RenderQuadContainerAsSprite(m_DirectionQuadContainerIndex, 0, ShowDirectionPos.x - Data.m_FontSizeDirection, ShowDirectionPos.y, Data.m_FontSizeDirection, Data.m_FontSizeDirection); } - else if(DirRight) + if(Data.m_DirJump) { Graphics()->TextureSet(g_pData->m_aImages[IMAGE_ARROW].m_Id); - Graphics()->QuadsSetRotation(0); - Graphics()->RenderQuadContainerAsSprite(m_DirectionQuadContainerIndex, 0, ShowDirectionPos.x + 30.f, ShowDirectionPos.y); + Graphics()->QuadsSetRotation(pi * 1.5f); + Graphics()->RenderQuadContainerAsSprite(m_DirectionQuadContainerIndex, 0, ShowDirectionPos.x, ShowDirectionPos.y - Data.m_FontSizeDirection / 2.0f, Data.m_FontSizeDirection, Data.m_FontSizeDirection); } - if(Jump) + if(Data.m_DirRight) { Graphics()->TextureSet(g_pData->m_aImages[IMAGE_ARROW].m_Id); - Graphics()->QuadsSetRotation(pi * 3 / 2); - Graphics()->RenderQuadContainerAsSprite(m_DirectionQuadContainerIndex, 0, ShowDirectionPos.x, ShowDirectionPos.y); + Graphics()->QuadsSetRotation(0.0f); + Graphics()->RenderQuadContainerAsSprite(m_DirectionQuadContainerIndex, 0, ShowDirectionPos.x + Data.m_FontSizeDirection, ShowDirectionPos.y, Data.m_FontSizeDirection, Data.m_FontSizeDirection); } Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); - Graphics()->QuadsSetRotation(0); + Graphics()->QuadsSetRotation(0.0f); } - // render name plate - if((!pPlayerInfo->m_Local || g_Config.m_ClNameplatesOwn) && g_Config.m_ClNameplates) + if((Data.m_pName && Data.m_pName[0] != '\0') || Data.m_ClientId >= 0 || Data.m_ShowFriendMark) { - float a; - if(g_Config.m_ClNameplatesAlways == 0) - a = clamp(1 - std::pow(distance(m_pClient->m_Controls.m_aTargetPos[g_Config.m_ClDummy], Position) / 200.0f, 16.0f), 0.0f, 1.0f); - else - a = 1.0f; + YOffset -= Data.m_FontSize; + NamePlate.m_Name.Update(*this, Data.m_ClientId, Data.m_pName, Data.m_ShowFriendMark, Data.m_FontSize); + if(NamePlate.m_Name.m_TextContainerIndex.Valid()) + TextRender()->RenderTextContainer(NamePlate.m_Name.m_TextContainerIndex, Color, OutlineColor, Data.m_Position.x - TextRender()->GetBoundingBoxTextContainer(NamePlate.m_Name.m_TextContainerIndex).m_W / 2.0f, YOffset); + } + if(Data.m_pClan && Data.m_pClan[0] != '\0') + { + YOffset -= Data.m_FontSizeClan; + NamePlate.m_Clan.Update(*this, Data.m_pClan, Data.m_FontSizeClan); + if(NamePlate.m_Clan.m_TextContainerIndex.Valid()) + TextRender()->RenderTextContainer(NamePlate.m_Clan.m_TextContainerIndex, Color, OutlineColor, Data.m_Position.x - TextRender()->GetBoundingBoxTextContainer(NamePlate.m_Clan.m_TextContainerIndex).m_W / 2.0f, YOffset); + } - if(str_comp(ClientData.m_aName, NamePlate.m_aName) != 0 || FontSize != NamePlate.m_NameTextFontSize) + if(Data.m_ShowHookWeakStrongId || (Data.m_ShowHookWeakStrong && Data.m_HookWeakStrong != TRISTATE::SOME)) // Don't show hook icon if there's no ID or hook strength to show + { + ColorRGBA HookWeakStrongColor; + int StrongWeakSpriteId; + switch(Data.m_HookWeakStrong) { - str_copy(NamePlate.m_aName, ClientData.m_aName); - NamePlate.m_NameTextFontSize = FontSize; - - CTextCursor Cursor; - TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_RENDER); - - // create nameplates at standard zoom - float ScreenX0, ScreenY0, ScreenX1, ScreenY1; - Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); - RenderTools()->MapScreenToInterface(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y); - TextRender()->RecreateTextContainer(NamePlate.m_NameTextContainerIndex, &Cursor, ClientData.m_aName); - Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); + case TRISTATE::ALL: + HookWeakStrongColor = color_cast(ColorHSLA(6401973)); + StrongWeakSpriteId = SPRITE_HOOK_STRONG; + break; + case TRISTATE::SOME: + HookWeakStrongColor = ColorRGBA(1.0f, 1.0f, 1.0f); + StrongWeakSpriteId = SPRITE_HOOK_ICON; + break; + case TRISTATE::NONE: + HookWeakStrongColor = color_cast(ColorHSLA(41131)); + StrongWeakSpriteId = SPRITE_HOOK_WEAK; + break; + default: + dbg_assert(false, "Invalid hook weak/strong state"); + dbg_break(); } + HookWeakStrongColor.a = Data.m_Alpha; - if(g_Config.m_ClNameplatesClan) + YOffset -= Data.m_FontSizeHookWeakStrong; + float ShowHookWeakStrongIdSize = 0.0f; + if(Data.m_ShowHookWeakStrongId) { - if(str_comp(ClientData.m_aClan, NamePlate.m_aClan) != 0 || FontSizeClan != NamePlate.m_ClanTextFontSize) + NamePlate.m_WeakStrongId.Update(*this, Data.m_HookWeakStrongId, Data.m_FontSizeHookWeakStrong); + if(NamePlate.m_WeakStrongId.m_TextContainerIndex.Valid()) { - str_copy(NamePlate.m_aClan, ClientData.m_aClan); - NamePlate.m_ClanTextFontSize = FontSizeClan; - - CTextCursor Cursor; - TextRender()->SetCursor(&Cursor, 0, 0, FontSizeClan, TEXTFLAG_RENDER); - - // create nameplates at standard zoom - float ScreenX0, ScreenY0, ScreenX1, ScreenY1; - Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); - RenderTools()->MapScreenToInterface(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y); - TextRender()->RecreateTextContainer(NamePlate.m_ClanTextContainerIndex, &Cursor, ClientData.m_aClan); - Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); + ShowHookWeakStrongIdSize = TextRender()->GetBoundingBoxTextContainer(NamePlate.m_WeakStrongId.m_TextContainerIndex).m_W; + float X = Data.m_Position.x - ShowHookWeakStrongIdSize / 2.0f; + if(Data.m_ShowHookWeakStrong) + X += Data.m_FontSizeHookWeakStrong * 0.75f; + TextRender()->TextColor(HookWeakStrongColor); + TextRender()->RenderTextContainer(NamePlate.m_WeakStrongId.m_TextContainerIndex, HookWeakStrongColor, OutlineColor, X, YOffset); } } - - if(g_Config.m_ClNameplatesTeamcolors) + if(Data.m_ShowHookWeakStrong) { - const int Team = m_pClient->m_Teams.Team(pPlayerInfo->m_ClientId); - if(Team) - { - rgb = m_pClient->GetDDTeamColor(Team, 0.75f); - } + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_STRONGWEAK].m_Id); + Graphics()->QuadsBegin(); + + Graphics()->SetColor(HookWeakStrongColor); + RenderTools()->SelectSprite(StrongWeakSpriteId); + + const float StrongWeakImgSize = Data.m_FontSizeHookWeakStrong * 1.5f; + float X = Data.m_Position.x; + if(Data.m_ShowHookWeakStrongId) + X -= ShowHookWeakStrongIdSize / 2.0f; + RenderTools()->DrawSprite(X, YOffset + StrongWeakImgSize / 2.7f, StrongWeakImgSize); + Graphics()->QuadsEnd(); } + } - ColorRGBA TColor; - ColorRGBA TOutlineColor; + TextRender()->TextColor(TextRender()->DefaultTextColor()); + TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor()); - if(OtherTeam && !ForceAlpha) - { - TOutlineColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.2f * g_Config.m_ClShowOthersAlpha / 100.0f); - TColor = rgb.WithAlpha(g_Config.m_ClShowOthersAlpha / 100.0f); - } - else - { - TOutlineColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f * a); - TColor = rgb.WithAlpha(a); - } - if(g_Config.m_ClNameplatesTeamcolors && m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_TEAMS) - { - if(ClientData.m_Team == TEAM_RED) - TColor = ColorRGBA(1.0f, 0.5f, 0.5f, a); - else if(ClientData.m_Team == TEAM_BLUE) - TColor = ColorRGBA(0.7f, 0.7f, 1.0f, a); - } + TextRender()->SetRenderFlags(0); +} + +void CNamePlates::RenderNamePlateGame(vec2 Position, const CNetObj_PlayerInfo *pPlayerInfo, float Alpha, bool ForceAlpha) +{ + CRenderNamePlateData Data; + + const auto &ClientData = m_pClient->m_aClients[pPlayerInfo->m_ClientId]; + const bool OtherTeam = m_pClient->IsOtherTeam(pPlayerInfo->m_ClientId); + + bool ShowNamePlate = pPlayerInfo->m_Local ? g_Config.m_ClNamePlatesOwn : g_Config.m_ClNamePlates; + + Data.m_Position = Position; + Data.m_ClientId = ShowNamePlate && g_Config.m_ClNamePlatesIds ? pPlayerInfo->m_ClientId : -1; + Data.m_pName = ShowNamePlate ? m_pClient->m_aClients[pPlayerInfo->m_ClientId].m_aName : nullptr; + Data.m_ShowFriendMark = ShowNamePlate && g_Config.m_ClNamePlatesFriendMark && m_pClient->m_aClients[pPlayerInfo->m_ClientId].m_Friend; + Data.m_FontSize = 18.0f + 20.0f * g_Config.m_ClNamePlatesSize / 100.0f; + + Data.m_pClan = ShowNamePlate && g_Config.m_ClNamePlatesClan ? m_pClient->m_aClients[pPlayerInfo->m_ClientId].m_aClan : nullptr; + Data.m_FontSizeClan = 18.0f + 20.0f * g_Config.m_ClNamePlatesClanSize / 100.0f; + + Data.m_FontSizeHookWeakStrong = 18.0f + 20.0f * g_Config.m_ClNamePlatesStrongSize / 100.0f; + Data.m_FontSizeDirection = 18.0f + 20.0f * g_Config.m_ClDirectionSize / 100.0f; + + Data.m_Alpha = Alpha; + if(!ForceAlpha) + { + if(g_Config.m_ClNamePlatesAlways == 0) + Data.m_Alpha *= clamp(1.0f - std::pow(distance(m_pClient->m_Controls.m_aTargetPos[g_Config.m_ClDummy], Position) / 200.0f, 16.0f), 0.0f, 1.0f); + if(OtherTeam) + Data.m_Alpha *= (float)g_Config.m_ClShowOthersAlpha / 100.0f; + } - TOutlineColor.a *= Alpha; - TColor.a *= Alpha; + Data.m_Color = ColorRGBA(1.0f, 1.0f, 1.0f); + Data.m_OutlineColor = ColorRGBA(0.0f, 0.0f, 0.0f); - if(NamePlate.m_NameTextContainerIndex.Valid()) + if(g_Config.m_ClNamePlatesTeamcolors) + { + if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_TEAMS) { - YOffset -= FontSize; - TextRender()->RenderTextContainer(NamePlate.m_NameTextContainerIndex, TColor, TOutlineColor, Position.x - TextRender()->GetBoundingBoxTextContainer(NamePlate.m_NameTextContainerIndex).m_W / 2.0f, YOffset); + if(ClientData.m_Team == TEAM_RED) + Data.m_Color = ColorRGBA(1.0f, 0.5f, 0.5f); + else if(ClientData.m_Team == TEAM_BLUE) + Data.m_Color = ColorRGBA(0.7f, 0.7f, 1.0f); } - - if(g_Config.m_ClNameplatesClan) + else { - YOffset -= FontSizeClan; - if(NamePlate.m_ClanTextContainerIndex.Valid()) - TextRender()->RenderTextContainer(NamePlate.m_ClanTextContainerIndex, TColor, TOutlineColor, Position.x - TextRender()->GetBoundingBoxTextContainer(NamePlate.m_ClanTextContainerIndex).m_W / 2.0f, YOffset); + const int Team = m_pClient->m_Teams.Team(pPlayerInfo->m_ClientId); + if(Team) + Data.m_Color = m_pClient->GetDDTeamColor(Team, 0.75f); } + } - if(g_Config.m_ClNameplatesFriendMark && ClientData.m_Friend) + int ShowDirectionConfig = g_Config.m_ClShowDirection; +#if defined(CONF_VIDEORECORDER) + if(IVideo::Current()) + ShowDirectionConfig = g_Config.m_ClVideoShowDirection; +#endif + Data.m_DirLeft = Data.m_DirJump = Data.m_DirRight = false; + switch(ShowDirectionConfig) + { + case 0: // off + Data.m_ShowDirection = false; + break; + case 1: // others + Data.m_ShowDirection = !pPlayerInfo->m_Local; + break; + case 2: // everyone + Data.m_ShowDirection = true; + break; + case 3: // only self + Data.m_ShowDirection = pPlayerInfo->m_Local; + break; + default: + dbg_assert(false, "ShowDirectionConfig invalid"); + dbg_break(); + } + if(Data.m_ShowDirection) + { + if(Client()->State() != IClient::STATE_DEMOPLAYBACK && pPlayerInfo->m_Local) // always render local input when not in demo playback { - YOffset -= FontSize; - ColorRGBA Color = ColorRGBA(1.0f, 0.0f, 0.0f, Alpha); - if(OtherTeam && !ForceAlpha) - Color.a *= g_Config.m_ClShowOthersAlpha / 100.0f; - else - Color.a *= a; - - const char *pFriendMark = "♥"; - TextRender()->TextColor(Color); - TextRender()->Text(Position.x - TextRender()->TextWidth(FontSize, pFriendMark) / 2.0f, YOffset, FontSize, pFriendMark); + const auto &InputData = m_pClient->m_Controls.m_aInputData[g_Config.m_ClDummy]; + Data.m_DirLeft = InputData.m_Direction == -1; + Data.m_DirJump = InputData.m_Jump == 1; + Data.m_DirRight = InputData.m_Direction == 1; } - - if(g_Config.m_Debug || g_Config.m_ClNameplatesIds) // render client id when in debug as well + else { - YOffset -= FontSize; - char aBuf[12]; - str_format(aBuf, sizeof(aBuf), "%d", pPlayerInfo->m_ClientId); - TextRender()->TextColor(rgb); - TextRender()->Text(Position.x - TextRender()->TextWidth(FontSize, aBuf) / 2.0f, YOffset, FontSize, aBuf); + const auto &Character = m_pClient->m_Snap.m_aCharacters[pPlayerInfo->m_ClientId]; + Data.m_DirLeft = Character.m_Cur.m_Direction == -1; + Data.m_DirJump = Character.m_Cur.m_Jumped & 1; + Data.m_DirRight = Character.m_Cur.m_Direction == 1; } } - if((g_Config.m_Debug || g_Config.m_ClNameplatesStrong) && g_Config.m_ClNameplates) + Data.m_ShowHookWeakStrong = g_Config.m_Debug || g_Config.m_ClNamePlatesStrong > 0; + Data.m_HookWeakStrong = TRISTATE::SOME; + Data.m_ShowHookWeakStrongId = false; + Data.m_HookWeakStrongId = false; + if(Data.m_ShowHookWeakStrong) { const bool Following = (m_pClient->m_Snap.m_SpecInfo.m_Active && !GameClient()->m_MultiViewActivated && m_pClient->m_Snap.m_SpecInfo.m_SpectatorId != SPEC_FREEVIEW); if(m_pClient->m_Snap.m_LocalClientId != -1 || Following) @@ -214,61 +317,52 @@ void CNamePlates::RenderNameplate(vec2 Position, const CNetObj_PlayerInfo *pPlay const CGameClient::CSnapState::CCharacterInfo &Other = m_pClient->m_Snap.m_aCharacters[pPlayerInfo->m_ClientId]; if(Selected.m_HasExtendedData && Other.m_HasExtendedData) { - if(SelectedId == pPlayerInfo->m_ClientId) - { - TextRender()->TextColor(rgb); - } - else - { - Graphics()->TextureSet(g_pData->m_aImages[IMAGE_STRONGWEAK].m_Id); - Graphics()->QuadsBegin(); - ColorRGBA StrongWeakStatusColor; - int StrongWeakSpriteId; - if(Selected.m_ExtendedData.m_StrongWeakId > Other.m_ExtendedData.m_StrongWeakId) - { - StrongWeakStatusColor = color_cast(ColorHSLA(6401973)); - StrongWeakSpriteId = SPRITE_HOOK_STRONG; - } - else - { - StrongWeakStatusColor = color_cast(ColorHSLA(41131)); - StrongWeakSpriteId = SPRITE_HOOK_WEAK; - } - - if(OtherTeam && !ForceAlpha) - StrongWeakStatusColor.a = g_Config.m_ClShowOthersAlpha / 100.0f; - else if(g_Config.m_ClNameplatesAlways == 0) - StrongWeakStatusColor.a = clamp(1 - std::pow(distance(m_pClient->m_Controls.m_aTargetPos[g_Config.m_ClDummy], Position) / 200.0f, 16.0f), 0.0f, 1.0f); - else - StrongWeakStatusColor.a = 1.0f; - - StrongWeakStatusColor.a *= Alpha; - Graphics()->SetColor(StrongWeakStatusColor); - float ScaleX, ScaleY; - RenderTools()->SelectSprite(StrongWeakSpriteId); - RenderTools()->GetSpriteScale(StrongWeakSpriteId, ScaleX, ScaleY); - TextRender()->TextColor(StrongWeakStatusColor); - - const float StrongWeakImgSize = 40.0f; - YOffset -= StrongWeakImgSize * ScaleY; - RenderTools()->DrawSprite(Position.x, YOffset + (StrongWeakImgSize / 2.0f) * ScaleY, StrongWeakImgSize); - Graphics()->QuadsEnd(); - } - if(g_Config.m_Debug || g_Config.m_ClNameplatesStrong == 2) - { - YOffset -= FontSize; - char aBuf[12]; - str_format(aBuf, sizeof(aBuf), "%d", Other.m_ExtendedData.m_StrongWeakId); - TextRender()->Text(Position.x - TextRender()->TextWidth(FontSize, aBuf) / 2.0f, YOffset, FontSize, aBuf); - } + if(SelectedId != pPlayerInfo->m_ClientId) + Data.m_HookWeakStrong = Selected.m_ExtendedData.m_StrongWeakId > Other.m_ExtendedData.m_StrongWeakId ? TRISTATE::ALL : TRISTATE::NONE; + Data.m_ShowHookWeakStrongId = g_Config.m_Debug || g_Config.m_ClNamePlatesStrong == 2; + if(Data.m_ShowHookWeakStrongId) + Data.m_HookWeakStrongId = Other.m_ExtendedData.m_StrongWeakId; } } } - TextRender()->TextColor(TextRender()->DefaultTextColor()); - TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor()); + GameClient()->m_NamePlates.RenderNamePlate(m_aNamePlates[pPlayerInfo->m_ClientId], Data); +} - TextRender()->SetRenderFlags(0); +void CNamePlates::RenderNamePlatePreview(vec2 Position) +{ + const float FontSize = 18.0f + 20.0f * g_Config.m_ClNamePlatesSize / 100.0f; + const float FontSizeClan = 18.0f + 20.0f * g_Config.m_ClNamePlatesClanSize / 100.0f; + + const float FontSizeDirection = 18.0f + 20.0f * g_Config.m_ClDirectionSize / 100.0f; + const float FontSizeHookWeakStrong = 18.0f + 20.0f * g_Config.m_ClNamePlatesStrongSize / 100.0f; + + CRenderNamePlateData Data; + + Data.m_Position = Position; + Data.m_Color = g_Config.m_ClNamePlatesTeamcolors ? m_pClient->GetDDTeamColor(13, 0.75f) : TextRender()->DefaultTextColor(); + Data.m_OutlineColor = TextRender()->DefaultTextOutlineColor(); + Data.m_Alpha = 1.0f; + Data.m_pName = g_Config.m_ClNamePlates ? g_Config.m_PlayerName : nullptr; + Data.m_ShowFriendMark = g_Config.m_ClNamePlates && g_Config.m_ClNamePlatesFriendMark; + Data.m_ClientId = g_Config.m_ClNamePlates && g_Config.m_ClNamePlatesIds ? 1 : -1; + Data.m_FontSize = FontSize; + Data.m_pClan = g_Config.m_ClNamePlates && g_Config.m_ClNamePlatesClan ? g_Config.m_PlayerClan : nullptr; + Data.m_FontSizeClan = FontSizeClan; + + Data.m_ShowDirection = g_Config.m_ClShowDirection != 0 ? true : false; + Data.m_DirLeft = Data.m_DirJump = Data.m_DirRight = true; + Data.m_FontSizeDirection = FontSizeDirection; + + Data.m_ShowHookWeakStrong = g_Config.m_ClNamePlatesStrong >= 1; + Data.m_HookWeakStrong = TRISTATE::ALL; + Data.m_ShowHookWeakStrongId = g_Config.m_ClNamePlatesStrong >= 2; + Data.m_HookWeakStrongId = 1; + Data.m_FontSizeHookWeakStrong = FontSizeHookWeakStrong; + + CNamePlate NamePlate; + GameClient()->m_NamePlates.RenderNamePlate(NamePlate, Data); + NamePlate.DeleteTextContainers(*TextRender()); } void CNamePlates::OnRender() @@ -281,7 +375,7 @@ void CNamePlates::OnRender() if(IVideo::Current()) ShowDirection = g_Config.m_ClVideoShowDirection; #endif - if(!g_Config.m_ClNameplates && ShowDirection == 0) + if(!g_Config.m_ClNamePlates && ShowDirection == 0) return; // get screen edges to avoid rendering offscreen @@ -289,11 +383,11 @@ void CNamePlates::OnRender() Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); // expand the edges to prevent popping in/out onscreen // - // it is assumed that the nameplate and all its components fit into a 800x800 box placed directly above the tee + // it is assumed that the namePlate and all its components fit into a 800x800 box placed directly above the tee // this may need to be changed or calculated differently in the future ScreenX0 -= 400; ScreenX1 += 400; - //ScreenY0 -= 0; + // ScreenY0 -= 0; ScreenY1 += 800; for(int i = 0; i < MAX_CLIENTS; i++) @@ -306,22 +400,22 @@ void CNamePlates::OnRender() if(m_pClient->m_aClients[i].m_SpecCharPresent) { - // Each player can also have a spec char whose nameplate is displayed independently + // Each player can also have a spec char whose namePlate is displayed independently const vec2 RenderPos = m_pClient->m_aClients[i].m_SpecChar; // don't render offscreen if(in_range(RenderPos.x, ScreenX0, ScreenX1) && in_range(RenderPos.y, ScreenY0, ScreenY1)) { - RenderNameplate(RenderPos, pInfo, 0.4f, true); + RenderNamePlateGame(RenderPos, pInfo, 0.4f, true); } } if(m_pClient->m_Snap.m_aCharacters[i].m_Active) { - // Only render nameplates for active characters + // Only render namePlates for active characters const vec2 RenderPos = m_pClient->m_aClients[i].m_RenderPos; // don't render offscreen if(in_range(RenderPos.x, ScreenX0, ScreenX1) && in_range(RenderPos.y, ScreenY0, ScreenY1)) { - RenderNameplate(RenderPos, pInfo, 1.0f, false); + RenderNamePlateGame(RenderPos, pInfo, 1.0f, false); } } } @@ -331,9 +425,7 @@ void CNamePlates::ResetNamePlates() { for(auto &NamePlate : m_aNamePlates) { - TextRender()->DeleteTextContainer(NamePlate.m_NameTextContainerIndex); - TextRender()->DeleteTextContainer(NamePlate.m_ClanTextContainerIndex); - + NamePlate.DeleteTextContainers(*TextRender()); NamePlate.Reset(); } } @@ -349,6 +441,6 @@ void CNamePlates::OnInit() // Quad for the direction arrows above the player m_DirectionQuadContainerIndex = Graphics()->CreateQuadContainer(false); - RenderTools()->QuadContainerAddSprite(m_DirectionQuadContainerIndex, 0.f, 0.f, 22.f); + RenderTools()->QuadContainerAddSprite(m_DirectionQuadContainerIndex, 0.0f, 0.0f, 1.0f); Graphics()->QuadContainerUpload(m_DirectionQuadContainerIndex); } diff --git a/src/game/client/components/nameplates.h b/src/game/client/components/nameplates.h index 7148dd7d56a..5c20f2473da 100644 --- a/src/game/client/components/nameplates.h +++ b/src/game/client/components/nameplates.h @@ -12,42 +12,130 @@ struct CNetObj_Character; struct CNetObj_PlayerInfo; -struct SPlayerNamePlate +class CNamePlates; + +class CNamePlate { - SPlayerNamePlate() +public: + class CNamePlateName + { + public: + CNamePlateName() + { + Reset(); + } + void Reset() + { + m_TextContainerIndex.Reset(); + m_Id = -1; + m_aName[0] = '\0'; + m_FriendMark = false; + m_FontSize = -INFINITY; + } + void Update(CNamePlates &This, int Id, const char *pName, bool FriendMark, float FontSize); + STextContainerIndex m_TextContainerIndex; + char m_aName[MAX_NAME_LENGTH]; + int m_Id; + bool m_FriendMark; + float m_FontSize; + }; + class CNamePlateClan + { + public: + CNamePlateClan() + { + Reset(); + } + void Reset() + { + m_TextContainerIndex.Reset(); + m_aClan[0] = '\0'; + m_FontSize = -INFINITY; + } + void Update(CNamePlates &This, const char *pClan, float FontSize); + STextContainerIndex m_TextContainerIndex; + char m_aClan[MAX_CLAN_LENGTH]; + float m_FontSize; + }; + class CNamePlateHookWeakStrongId + { + public: + CNamePlateHookWeakStrongId() + { + Reset(); + } + void Reset() + { + m_TextContainerIndex.Reset(); + m_Id = -1; + m_FontSize = -INFINITY; + } + void Update(CNamePlates &This, int Id, float FontSize); + STextContainerIndex m_TextContainerIndex; + int m_Id; + float m_FontSize; + }; + CNamePlate() { Reset(); } - void Reset() { - m_NameTextContainerIndex.Reset(); - m_ClanTextContainerIndex.Reset(); - m_aName[0] = '\0'; - m_aClan[0] = '\0'; - m_NameTextFontSize = m_ClanTextFontSize = 0.0f; + m_Name.Reset(); + m_Clan.Reset(); + m_WeakStrongId.Reset(); } - - char m_aName[MAX_NAME_LENGTH]; - STextContainerIndex m_NameTextContainerIndex; - float m_NameTextFontSize; - - char m_aClan[MAX_CLAN_LENGTH]; - STextContainerIndex m_ClanTextContainerIndex; - float m_ClanTextFontSize; + void DeleteTextContainers(ITextRender &TextRender) + { + TextRender.DeleteTextContainer(m_Name.m_TextContainerIndex); + TextRender.DeleteTextContainer(m_Clan.m_TextContainerIndex); + TextRender.DeleteTextContainer(m_WeakStrongId.m_TextContainerIndex); + } + CNamePlateName m_Name; + CNamePlateClan m_Clan; + CNamePlateHookWeakStrongId m_WeakStrongId; }; class CNamePlates : public CComponent { - void RenderNameplate(vec2 Position, const CNetObj_PlayerInfo *pPlayerInfo, float Alpha, bool ForceAlpha); + friend class CNamePlate::CNamePlateName; + friend class CNamePlate::CNamePlateClan; + friend class CNamePlate::CNamePlateHookWeakStrongId; - SPlayerNamePlate m_aNamePlates[MAX_CLIENTS]; + CNamePlate m_aNamePlates[MAX_CLIENTS]; void ResetNamePlates(); int m_DirectionQuadContainerIndex; + class CRenderNamePlateData + { + public: + vec2 m_Position; + ColorRGBA m_Color; + ColorRGBA m_OutlineColor; + float m_Alpha; + const char *m_pName; + bool m_ShowFriendMark; + int m_ClientId; + float m_FontSize; + const char *m_pClan; + float m_FontSizeClan; + bool m_ShowDirection; + bool m_DirLeft; + bool m_DirJump; + bool m_DirRight; + float m_FontSizeDirection; + bool m_ShowHookWeakStrong; + TRISTATE m_HookWeakStrong; + bool m_ShowHookWeakStrongId; + int m_HookWeakStrongId; + float m_FontSizeHookWeakStrong; + }; + void RenderNamePlate(CNamePlate &NamePlate, const CRenderNamePlateData &Data); public: + void RenderNamePlateGame(vec2 Position, const CNetObj_PlayerInfo *pPlayerInfo, float Alpha, bool ForceAlpha); + void RenderNamePlatePreview(vec2 Position); virtual int Sizeof() const override { return sizeof(*this); } virtual void OnWindowResize() override; virtual void OnInit() override; From eb0112dee220d1d6a469893bc4ace8ba5dba47bf Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Tue, 10 Dec 2024 14:04:29 +0800 Subject: [PATCH 09/40] Add logger for android cmake script --- scripts/android/cmake_android.sh | 64 +++++++++++++++++++------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/scripts/android/cmake_android.sh b/scripts/android/cmake_android.sh index 2e91565bc41..f2720d49971 100755 --- a/scripts/android/cmake_android.sh +++ b/scripts/android/cmake_android.sh @@ -29,51 +29,63 @@ COLOR_RESET="\e[0m" SHOW_USAGE_INFO=0 +log_info() { + printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "$1" +} + +log_warn() { + printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "$1" 1>&2 +} + +log_error() { + printf "${COLOR_RED}%s${COLOR_RESET}\n" "$1" 1>&2 +} + if [ -z ${1+x} ]; then SHOW_USAGE_INFO=1 - printf "${COLOR_RED}%s${COLOR_RESET}\n" "Did not pass Android build type" + log_error "Did not pass Android build type" else ANDROID_BUILD=$1 if [[ "${ANDROID_BUILD}" == "x64" ]]; then ANDROID_BUILD="x86_64" fi - printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Android build type: ${ANDROID_BUILD}" + log_warn "Android build type: ${ANDROID_BUILD}" fi if [ -z ${2+x} ]; then SHOW_USAGE_INFO=1 - printf "${COLOR_RED}%s${COLOR_RESET}\n" "Did not pass game name" + log_error "Did not pass game name" else GAME_NAME=$2 - printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Game name: ${GAME_NAME}" + log_warn "Game name: ${GAME_NAME}" fi if [ -z ${3+x} ]; then SHOW_USAGE_INFO=1 - printf "${COLOR_RED}%s${COLOR_RESET}\n" "Did not pass package name" + log_error "Did not pass package name" else PACKAGE_NAME=$3 - printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Package name: ${PACKAGE_NAME}" + log_warn "Package name: ${PACKAGE_NAME}" fi if [ -z ${4+x} ]; then SHOW_USAGE_INFO=1 - printf "${COLOR_RED}%s${COLOR_RESET}\n" "Did not pass build type" + log_error "Did not pass build type" else BUILD_TYPE=$4 - printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Build type: ${BUILD_TYPE}" + log_warn "Build type: ${BUILD_TYPE}" fi if [ -z ${5+x} ]; then SHOW_USAGE_INFO=1 - printf "${COLOR_RED}%s${COLOR_RESET}\n" "Did not pass build folder" + log_error "Did not pass build folder" else BUILD_FOLDER=$5 - printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Build folder: ${BUILD_FOLDER}" + log_warn "Build folder: ${BUILD_FOLDER}" fi if [ $SHOW_USAGE_INFO == 1 ]; then - printf "${COLOR_RED}%s${COLOR_RESET}\n" "Usage: ./cmake_android.sh " + log_error "Usage: ./cmake_android.sh " exit 1 fi @@ -85,17 +97,17 @@ DEFAULT_KEY_PW=android DEFAULT_KEY_ALIAS=androiddebugkey if [ -z ${TW_KEY_NAME+x} ]; then - printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Did not pass a key path for the APK signer, using default: ${DEFAULT_KEY_NAME}" + log_warn "Did not pass a key path for the APK signer, using default: ${DEFAULT_KEY_NAME}" else DEFAULT_KEY_NAME=$TW_KEY_NAME fi if [ -z ${TW_KEY_PW+x} ]; then - printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Did not pass a key password for the APK signer, using default: ${DEFAULT_KEY_PW}" + log_warn "Did not pass a key password for the APK signer, using default: ${DEFAULT_KEY_PW}" else DEFAULT_KEY_PW=$TW_KEY_PW fi if [ -z ${TW_KEY_ALIAS+x} ]; then - printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Did not pass a key alias for the APK signer, using default: ${DEFAULT_KEY_ALIAS}" + log_warn "Did not pass a key alias for the APK signer, using default: ${DEFAULT_KEY_ALIAS}" else DEFAULT_KEY_ALIAS=$TW_KEY_ALIAS fi @@ -110,7 +122,7 @@ if [ -z ${TW_VERSION_CODE+x} ]; then if [ -z ${ANDROID_VERSION_CODE+x} ]; then ANDROID_VERSION_CODE=1 fi - printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Did not pass a version code, using default: ${ANDROID_VERSION_CODE}" + log_warn "Did not pass a version code, using default: ${ANDROID_VERSION_CODE}" else ANDROID_VERSION_CODE=$TW_VERSION_CODE fi @@ -123,7 +135,7 @@ if [ -z ${TW_VERSION_NAME+x} ]; then if [ -z ${ANDROID_VERSION_NAME+x} ]; then ANDROID_VERSION_NAME="1.0" fi - printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Did not pass a version name, using default: ${ANDROID_VERSION_NAME}" + log_warn "Did not pass a version name, using default: ${ANDROID_VERSION_NAME}" else ANDROID_VERSION_NAME=$TW_VERSION_NAME fi @@ -165,26 +177,26 @@ function build_for_type() { mkdir -p "${BUILD_FOLDER}" if [[ "${ANDROID_BUILD}" == "arm" || "${ANDROID_BUILD}" == "all" ]]; then - printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Building cmake (arm)..." + log_info "Building cmake (arm)..." build_for_type arm armeabi-v7a armv7-linux-androideabi fi if [[ "${ANDROID_BUILD}" == "arm64" || "${ANDROID_BUILD}" == "all" ]]; then - printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Building cmake (arm64)..." + log_info "Building cmake (arm64)..." build_for_type arm64 arm64-v8a aarch64-linux-android fi if [[ "${ANDROID_BUILD}" == "x86" || "${ANDROID_BUILD}" == "all" ]]; then - printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Building cmake (x86)..." + log_info "Building cmake (x86)..." build_for_type x86 x86 i686-linux-android fi if [[ "${ANDROID_BUILD}" == "x86_64" || "${ANDROID_BUILD}" == "all" ]]; then - printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Building cmake (x86_64)..." + log_info "Building cmake (x86_64)..." build_for_type x86_64 x86_64 x86_64-linux-android fi -printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Copying project files..." +log_info "Copying project files..." cd "${BUILD_FOLDER}" || exit 1 @@ -211,7 +223,7 @@ copy_dummy_files scripts/android/files/res/xml/shortcuts.xml src/main/res/xml/sh copy_dummy_files other/icons/DDNet_256x256x32.png src/main/res/mipmap/ic_launcher.png copy_dummy_files other/icons/DDNet_256x256x32.png src/main/res/mipmap/ic_launcher_round.png -printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Copying libraries..." +log_info "Copying libraries..." function copy_libs() { mkdir -p "lib/$2" @@ -239,15 +251,15 @@ if [[ "${ANDROID_BUILD}" == "all" ]]; then ANDROID_BUILD_DUMMY=arm fi -printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Copying data folder..." +log_info "Copying data folder..." mkdir -p assets/asset_integrity_files cp -R "$ANDROID_SUB_BUILD_DIR/$ANDROID_BUILD_DUMMY/data" ./assets/asset_integrity_files -printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Downloading certificate..." +log_info "Downloading certificate..." curl -s -S --remote-name --time-cond cacert.pem https://curl.se/ca/cacert.pem cp ./cacert.pem ./assets/asset_integrity_files/data/cacert.pem || exit 1 -printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Creating integrity index file..." +log_info "Creating integrity index file..." ( cd assets/asset_integrity_files || exit 1 tmpfile="$(mktemp /tmp/hash_strings.XXX)" @@ -261,7 +273,7 @@ printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Creating integrity index file..." } > "integrity.txt" ) -printf "${COLOR_CYAN}%s${COLOR_RESET}\n" "Preparing gradle build..." +log_info "Preparing gradle build..." rm -R -f src/main/java/org mkdir -p src/main/java From 68c39c6c6327129934adb1f59af329cd8f2571e4 Mon Sep 17 00:00:00 2001 From: Patiga Date: Tue, 10 Dec 2024 16:50:07 +0100 Subject: [PATCH 10/40] Implementation note for dilate: Unused sum&counter The code is confusing, as the sum and counter are not used. Establishing a different dilate algorithm would be a hassle. The difference is minor and probably not noticeable. --- src/engine/gfx/image_manipulation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/engine/gfx/image_manipulation.cpp b/src/engine/gfx/image_manipulation.cpp index 3b7ab1aac8e..ad4bcc91a3a 100644 --- a/src/engine/gfx/image_manipulation.cpp +++ b/src/engine/gfx/image_manipulation.cpp @@ -101,6 +101,10 @@ static void Dilate(int w, int h, const uint8_t *pSrc, uint8_t *pDest) if(pSrc[m + DILATE_BPP - 1] > DILATE_ALPHA_THRESHOLD) continue; + // --- Implementation Note --- + // The sum and counter variable can be used to compute a smoother dilated image. + // In this reference implementation, the loop breaks as soon as Counter == 1. + // We break the loop here to match the selection of the previously used algorithm. int aSumOfOpaque[] = {0, 0, 0}; int Counter = 0; for(int c = 0; c < 4; c++) From bdc97e7d5b64e6bf022359d2707c7f29cbf0e808 Mon Sep 17 00:00:00 2001 From: KebsCS Date: Tue, 10 Dec 2024 20:32:00 +0100 Subject: [PATCH 11/40] Fix dummy directions nameplate when frozen --- src/game/client/components/nameplates.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index 138c5d67c7c..f6d8cf710c7 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -287,7 +287,15 @@ void CNamePlates::RenderNamePlateGame(vec2 Position, const CNetObj_PlayerInfo *p } if(Data.m_ShowDirection) { - if(Client()->State() != IClient::STATE_DEMOPLAYBACK && pPlayerInfo->m_Local) // always render local input when not in demo playback + if(Client()->State() != IClient::STATE_DEMOPLAYBACK && + Client()->DummyConnected() && pPlayerInfo->m_ClientId == m_pClient->m_aLocalIds[!g_Config.m_ClDummy]) + { + const auto &InputData = m_pClient->m_Controls.m_aInputData[!g_Config.m_ClDummy]; + Data.m_DirLeft = InputData.m_Direction == -1; + Data.m_DirJump = InputData.m_Jump == 1; + Data.m_DirRight = InputData.m_Direction == 1; + } + else if(Client()->State() != IClient::STATE_DEMOPLAYBACK && pPlayerInfo->m_Local) // always render local input when not in demo playback { const auto &InputData = m_pClient->m_Controls.m_aInputData[g_Config.m_ClDummy]; Data.m_DirLeft = InputData.m_Direction == -1; From a02d3b1d4a40c9883ac0cbedf85d49b7f0d0250e Mon Sep 17 00:00:00 2001 From: RRRmnik Date: Wed, 11 Dec 2024 16:42:52 +0400 Subject: [PATCH 12/40] russian.txt translations --- data/languages/russian.txt | 67 +++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/data/languages/russian.txt b/data/languages/russian.txt index 1adb0c44d57..b020c3c78a4 100644 --- a/data/languages/russian.txt +++ b/data/languages/russian.txt @@ -21,6 +21,7 @@ # ByFox 2023-11-14 11:24:00 # Sedonya 2024-06-20 03:18:10 # WTF 2024-11-03 15:57:20 +# Риксед 2024-12-11 16:34:00 ##### /authors ##### ##### translated strings ##### @@ -1951,105 +1952,105 @@ Eyes == Глаза Online friends (%d) -== +== Друзья в сети (%d) Add friends by entering their name below or by clicking their name in the player list. -== +== Чтобы добавить друзей, впишите их имена ниже или нажимая по их имени в списке игроков. Add clanmates by entering their clan below and leaving the name blank. -== +== Чтобы добавить соклановцев, впишите их клан и оставьте имя пустым. Offline friends and clanmates will appear here. -== +== Друзья не в сети и соклановцы появятся здесь. Edit touch controls -== +== Изменить сенсорное управление Close -== +== Закрыть Save changes -== +== Сохранить изменение Error saving touch controls -== +== Ошибка при сохранении сенсорного управления Could not save touch controls to file. See local console for details. -== +== Не удалось сохранить сенсорное управление в файл. Просмотрите локальную консоль для подробностей. Unsaved changes -== +== Несохранённые изменения Discard changes -== +== Отменить изменения Are you sure that you want to discard the current changes to the touch controls? -== +== Вы уверены что хотите отменить изменения сенсорного управления? Are you sure that you want to reset the touch controls to default? -== +== Вы уверены, что хотите сбросить сенсорное управление Import from clipboard -== +== Импортировать из буфера обмена Are you sure that you want to import the touch controls from the clipboard? This will overwrite your current touch controls. -== +== Вы уверены, что хотите импортировать сенсорное управление из буфера обмена? Это перезапишет ваше действующее сенсорное управление. Export to clipboard -== +== Экспортировать в буфер обмена Direct touch input while ingame -== +== Сенсорный ввод напрямую пока вы находитесь в игре [Direct touch input] Disabled -== +== Отменено [Direct touch input] Active action -== +== Активное действие [Direct touch input] Aim -== +== Прицеливаться [Direct touch input] Fire -== +== Стрелять [Direct touch input] Hook -== +== Крюк Direct touch input while spectating -== +== Сенсорный ввод напрямую пока вы в режиме наблюдателя Error loading touch controls -== +== Ошибка при загрузке сенсорного управления Could not load touch controls from file. See local console for details. -== +== Не удалось загрузить сенсорное управление из файла. Просмотрите локальную консоль для подробностей. Could not load default touch controls from file. See local console for details. -== +== Не удалось загрузить стандартное сенсорное управление из файла. Просмотрите локальную консоль для подробностей. Could not load touch controls from clipboard. See local console for details. -== +== Не удалось загрузить сенсорное управление из буфера обмена. Просмотрите локальную консоль для подробностей. Width of your own hook collision line -== +== Ширина вашей линии коллизий крюка Width of others' hook collision line -== +== Ширина чужих линий коллизий крюка Preview 'Hook collisions' being pressed -== +== Предпросмотр нажатия 'Коллизий крюка' Aim -== +== Прицеливаться Active: Fire -== +== Активно: Стрелять Active: Hook -== +== Активно: Крюк From 01f97fddd7ab40f96f24de38ea600ff83cfa92fb Mon Sep 17 00:00:00 2001 From: RRRmnik Date: Wed, 11 Dec 2024 16:59:21 +0400 Subject: [PATCH 13/40] Update russian.txt --- data/languages/russian.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/languages/russian.txt b/data/languages/russian.txt index b020c3c78a4..3ec07fef471 100644 --- a/data/languages/russian.txt +++ b/data/languages/russian.txt @@ -1955,7 +1955,7 @@ Online friends (%d) == Друзья в сети (%d) Add friends by entering their name below or by clicking their name in the player list. -== Чтобы добавить друзей, впишите их имена ниже или нажимая по их имени в списке игроков. +== Чтобы добавить друзей, впишите их имена ниже или нажимите по их имени в списке игроков. Add clanmates by entering their clan below and leaving the name blank. == Чтобы добавить соклановцев, впишите их клан и оставьте имя пустым. From 7886a736a7b7385d631f8707fbf380af1357aec9 Mon Sep 17 00:00:00 2001 From: noKetchup <93238471+n0Ketchp@users.noreply.github.com> Date: Wed, 11 Dec 2024 18:44:50 -0300 Subject: [PATCH 14/40] Update spanish.txt for 18.8 --- data/languages/spanish.txt | 68 +++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/data/languages/spanish.txt b/data/languages/spanish.txt index ab307ea5821..f8a8d02dde5 100644 --- a/data/languages/spanish.txt +++ b/data/languages/spanish.txt @@ -1951,108 +1951,108 @@ Eyes == Ojos Some fonts could not be loaded. Check the local console for details. -== +== No se pudieron cargar algunas fuentes. Revisa la consola local para más detalles. Online friends (%d) -== +== Amigos en línea (%d) Add friends by entering their name below or by clicking their name in the player list. -== +== Agrega amigos ingresando su nombre aquí abajo o haciendo clic en su nombre en la lista de jugadores. Add clanmates by entering their clan below and leaving the name blank. -== +== Agrega compañeros de clan ingresando el nombre del clan aquí abajo y dejando el campo del nombre en blanco. Offline friends and clanmates will appear here. -== +== Tus amigos y compañeros de clan que estén desconectados aparecerán aquí. Edit touch controls -== +== Editar controles táctiles Close -== +== Cerrar Save changes -== +== Guardar cambios Error saving touch controls -== +== Error al guardar los controles táctiles Could not save touch controls to file. See local console for details. -== +== No se pudieron guardar los controles táctiles. Revisa la consola local para más detalles Unsaved changes -== +== Cambios sin guardar Discard changes -== +== Descartar cambios Are you sure that you want to discard the current changes to the touch controls? -== +== ¿Estás seguro de que quieres descartar los cambios actuales a los controles táctiles? Are you sure that you want to reset the touch controls to default? -== +== ¿Estás seguro de que quieres restablecer los controles táctiles a su configuración predeterminada? Import from clipboard -== +== Importar desde el portapapeles Are you sure that you want to import the touch controls from the clipboard? This will overwrite your current touch controls. -== +== ¿Estás seguro de que quieres importar los controles táctiles desde el portapapeles? Esto sobreescribirá tus controles táctiles actuales. Export to clipboard -== +== Exportar al portapapeles Direct touch input while ingame -== +== Entrada táctil directa dentro del juego [Direct touch input] Disabled -== +== Desactivado [Direct touch input] Active action -== +== Acción activa [Direct touch input] Aim -== +== Apuntar [Direct touch input] Fire -== +== Disparar [Direct touch input] Hook -== +== Gancho Direct touch input while spectating -== +== Entrada táctil directa en espectador Error loading touch controls -== +== Error al cargar los controles táctiles Could not load touch controls from file. See local console for details. -== +== No se pudieron cargar los controles táctiles desde el archivo. Revisa la consola local para más detalles. Could not load default touch controls from file. See local console for details. -== +== No se pudieron cargar los controles táctiles predeterminados desde el archivo. Revisa la consola local para más detalles. Could not load touch controls from clipboard. See local console for details. -== +== No se pudieron cargar los controles táctiles desde el portapapeles. Revisa la consola local para más detalles. Width of your own hook collision line -== +== Ancho de tu propia línea de colisión del gancho Width of others' hook collision line -== +== Ancho de la línea de colisión del gancho de los demás Preview 'Hook collisions' being pressed -== +== Vista previa al presionar 'Colisiones de gancho' Aim -== +== Apuntar Active: Fire -== +== Activo: Disparar Active: Hook -== +== Activo: Gancho From 11ee49fc038375334ede619f44d887c29291b67e Mon Sep 17 00:00:00 2001 From: SollyBunny Date: Thu, 12 Dec 2024 04:17:51 +0000 Subject: [PATCH 15/40] Don't merge diffrent colored client msgs --- src/game/client/components/chat.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index a011bbb8522..0c357ec8836 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -722,13 +722,18 @@ void CChat::AddLine(int ClientId, int Team, const char *pLine) Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, pFrom, aBuf, ChatLogColor); }; + // Custom color for new line + std::optional CustomColor = std::nullopt; + if(ClientId == CLIENT_MSG) + CustomColor = color_cast(ColorHSLA(g_Config.m_ClMessageClientColor)); + CLine *pCurrentLine = &m_aLines[m_CurrentLine]; // Team Number: // 0 = global; 1 = team; 2 = sending whisper; 3 = receiving whisper // If it's a client message, m_aText will have ": " prepended so we have to work around it. - if(pCurrentLine->m_TeamNumber == Team && pCurrentLine->m_ClientId == ClientId && str_comp(pCurrentLine->m_aText, pLine) == 0) + if(pCurrentLine->m_TeamNumber == Team && pCurrentLine->m_ClientId == ClientId && str_comp(pCurrentLine->m_aText, pLine) == 0 && pCurrentLine->m_CustomColor == CustomColor) { pCurrentLine->m_TimesRepeated++; TextRender()->DeleteTextContainer(pCurrentLine->m_TextContainerIndex); @@ -755,7 +760,7 @@ void CChat::AddLine(int ClientId, int Team, const char *pLine) pCurrentLine->m_NameColor = -2; pCurrentLine->m_Friend = false; pCurrentLine->m_HasRenderTee = false; - pCurrentLine->m_CustomColor = std::nullopt; + pCurrentLine->m_CustomColor = CustomColor; TextRender()->DeleteTextContainer(pCurrentLine->m_TextContainerIndex); Graphics()->DeleteQuadContainer(pCurrentLine->m_QuadContainerIndex); @@ -789,8 +794,6 @@ void CChat::AddLine(int ClientId, int Team, const char *pLine) { str_copy(pCurrentLine->m_aName, "— "); str_copy(pCurrentLine->m_aText, pLine); - // Set custom color - pCurrentLine->m_CustomColor = color_cast(ColorHSLA(g_Config.m_ClMessageClientColor)); } else { From eaa893da058a8c6ec1dd63d9a6ae8c54cfda79bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 12 Dec 2024 21:37:24 +0100 Subject: [PATCH 16/40] Minor refactoring of server vote handling Avoid local references and nested assignment for readability when sending vote messages. Remove unnecessary temporary variable. Remove dead code. --- src/game/server/gamecontext.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 6c319fbac9f..4230a4f9b6d 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -814,16 +814,17 @@ void CGameContext::SendVoteSet(int ClientId) { Msg6.m_Timeout = Msg7.m_Timeout = (m_VoteCloseTime - time_get()) / time_freq(); Msg6.m_pDescription = m_aVoteDescription; - Msg7.m_pDescription = m_aSixupVoteDescription; Msg6.m_pReason = Msg7.m_pReason = m_aVoteReason; - int &Type = (Msg7.m_Type = protocol7::VOTE_UNKNOWN); + Msg7.m_pDescription = m_aSixupVoteDescription; if(IsKickVote()) - Type = protocol7::VOTE_START_KICK; + Msg7.m_Type = protocol7::VOTE_START_KICK; else if(IsSpecVote()) - Type = protocol7::VOTE_START_SPEC; + Msg7.m_Type = protocol7::VOTE_START_SPEC; else if(IsOptionVote()) - Type = protocol7::VOTE_START_OP; + Msg7.m_Type = protocol7::VOTE_START_OP; + else + Msg7.m_Type = protocol7::VOTE_UNKNOWN; } else { @@ -831,13 +832,14 @@ void CGameContext::SendVoteSet(int ClientId) Msg6.m_pDescription = Msg7.m_pDescription = ""; Msg6.m_pReason = Msg7.m_pReason = ""; - int &Type = (Msg7.m_Type = protocol7::VOTE_UNKNOWN); if(m_VoteEnforce == VOTE_ENFORCE_NO || m_VoteEnforce == VOTE_ENFORCE_NO_ADMIN) - Type = protocol7::VOTE_END_FAIL; + Msg7.m_Type = protocol7::VOTE_END_FAIL; else if(m_VoteEnforce == VOTE_ENFORCE_YES || m_VoteEnforce == VOTE_ENFORCE_YES_ADMIN) - Type = protocol7::VOTE_END_PASS; + Msg7.m_Type = protocol7::VOTE_END_PASS; else if(m_VoteEnforce == VOTE_ENFORCE_ABORT || m_VoteEnforce == VOTE_ENFORCE_CANCEL) - Type = protocol7::VOTE_END_ABORT; + Msg7.m_Type = protocol7::VOTE_END_ABORT; + else + Msg7.m_Type = protocol7::VOTE_UNKNOWN; if(m_VoteEnforce == VOTE_ENFORCE_NO_ADMIN || m_VoteEnforce == VOTE_ENFORCE_YES_ADMIN) Msg7.m_ClientId = -1; @@ -1218,7 +1220,6 @@ void CGameContext::OnTick() EndVote(); SendChat(-1, TEAM_ALL, "Vote failed enforced by authorized player", -1, FLAG_SIX); } - //else if(m_VoteEnforce == VOTE_ENFORCE_NO || time_get() > m_VoteCloseTime) else if(m_VoteEnforce == VOTE_ENFORCE_NO || (time_get() > m_VoteCloseTime && g_Config.m_SvVoteMajority)) { EndVote(); @@ -2478,9 +2479,7 @@ void CGameContext::OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientId) if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry + Server()->TickSpeed() * 3 > Server()->Tick()) return; - int64_t Now = Server()->Tick(); - - pPlayer->m_LastVoteTry = Now; + pPlayer->m_LastVoteTry = Server()->Tick(); pPlayer->UpdatePlaytime(); if(!pMsg->m_Vote) From 8f5f001fc771225a2f6994ee99de8be1fc02ade1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 11 Dec 2024 22:46:52 +0100 Subject: [PATCH 17/40] Fix server handling when vote creator leaves Reset the vote creator client ID `CGameContext::m_VoteCreator` to `-1` when the player that started the vote leaves instead of keeping the invalid client ID around. Previously, this was causing the server to crash due an assertion in the `IServer::GetAuthedState` function when a ban vote was aborted due to rcon authentication change being handled in the `CGameContext::OnSetAuthed` function after the vote creator has already left the server. It was possible to cause this situation as the target of a vote by executing `rcon_auth ""; rcon "kick "; rcon "logout"` (`ban` works for the same effect). This also caused existing votes to use the wrong vote creator if the same client ID was reused by a new client while a vote from a client that left is still running. Ban votes are now only aborted in the `CGameContext::OnSetAuthed` function when 1) a vote is actually active (this was previously not checked), 2) a client logged in (previously logout was also affected), and 3) the vote creator is unset or has a lower authentication level than the player logging in (previously used invalid vote creator). In particular also improve the handling for the `random_map` and `random_unfinished_map` commands when the vote creator leaves. Use the name `nameless tee` instead of `(invalid)` (returned by `IServer::ClientName`) as the requesting player name in queries. Handle the requesting player not being set when sending the result message. Avoid overriding the `m_VoteCreator` value in the callback of the `random_unfinished_map` command, which is not necessary and would cause incorrect vote behavior when the command is used while a vote is active. Also ensure that the voting state is initialized properly on server start. Closes #9374. --- src/game/server/gamecontext.cpp | 55 ++++++++++++++++++++++----------- src/game/server/score.cpp | 4 +-- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 4230a4f9b6d..7a03e2fb7d4 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -86,12 +86,9 @@ void CGameContext::Construct(int Resetting) mem_zero(&m_aPlayerHasInput, sizeof(m_aPlayerHasInput)); m_pController = 0; - m_aVoteCommand[0] = 0; - m_VoteType = VOTE_TYPE_UNKNOWN; - m_VoteCloseTime = 0; + m_pVoteOptionFirst = 0; m_pVoteOptionLast = 0; - m_NumVoteOptions = 0; m_LastMapVote = 0; m_SqlRandomMapResult = nullptr; @@ -100,6 +97,18 @@ void CGameContext::Construct(int Resetting) m_NumMutes = 0; m_NumVoteMutes = 0; + m_VoteCreator = -1; + m_VoteType = VOTE_TYPE_UNKNOWN; + m_VoteCloseTime = 0; + m_VoteUpdate = false; + m_VotePos = 0; + m_aVoteDescription[0] = '\0'; + m_aSixupVoteDescription[0] = '\0'; + m_aVoteCommand[0] = '\0'; + m_aVoteReason[0] = '\0'; + m_NumVoteOptions = 0; + m_VoteEnforce = VOTE_ENFORCE_UNKNOWN; + m_LatestLog = 0; mem_zero(&m_aLogs, sizeof(m_aLogs)); @@ -1074,7 +1083,14 @@ void CGameContext::OnTick() else if(m_VoteEnforce == VOTE_ENFORCE_CANCEL) { char aBuf[64]; - str_format(aBuf, sizeof(aBuf), "'%s' canceled their vote", Server()->ClientName(m_VoteCreator)); + if(m_VoteCreator == -1) + { + str_copy(aBuf, "Vote canceled"); + } + else + { + str_format(aBuf, sizeof(aBuf), "'%s' canceled their vote", Server()->ClientName(m_VoteCreator)); + } SendChat(-1, TEAM_ALL, aBuf); EndVote(); } @@ -1206,7 +1222,7 @@ void CGameContext::OnTick() EndVote(); SendChat(-1, TEAM_ALL, "Vote passed", -1, FLAG_SIX); - if(m_apPlayers[m_VoteCreator] && !IsKickVote() && !IsSpecVote()) + if(m_VoteCreator != -1 && m_apPlayers[m_VoteCreator] && !IsKickVote() && !IsSpecVote()) m_apPlayers[m_VoteCreator]->m_LastVoteCall = 0; } else if(m_VoteEnforce == VOTE_ENFORCE_YES_ADMIN) @@ -1282,7 +1298,7 @@ void CGameContext::OnTick() { if(m_SqlRandomMapResult->m_Success) { - if(PlayerExists(m_SqlRandomMapResult->m_ClientId) && m_SqlRandomMapResult->m_aMessage[0] != '\0') + if(m_SqlRandomMapResult->m_ClientId != -1 && m_apPlayers[m_SqlRandomMapResult->m_ClientId] && m_SqlRandomMapResult->m_aMessage[0] != '\0') SendChat(-1, TEAM_ALL, m_SqlRandomMapResult->m_aMessage); if(m_SqlRandomMapResult->m_aMap[0] != '\0') Server()->ChangeMap(m_SqlRandomMapResult->m_aMap); @@ -1724,6 +1740,10 @@ void CGameContext::OnClientDrop(int ClientId, const char *pReason) m_aTeamMapping[ClientId] = -1; m_VoteUpdate = true; + if(m_VoteCreator == ClientId) + { + m_VoteCreator = -1; + } // update spectator modes for(auto &pPlayer : m_apPlayers) @@ -3133,20 +3153,18 @@ void CGameContext::ConRandomMap(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; - int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1; - - pSelf->m_pScore->RandomMap(pSelf->m_VoteCreator, Stars); + const int ClientId = pResult->m_ClientId == -1 ? pSelf->m_VoteCreator : pResult->m_ClientId; + const int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1; + pSelf->m_pScore->RandomMap(ClientId, Stars); } void CGameContext::ConRandomUnfinishedMap(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; - int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1; - if(pResult->m_ClientId != -1) - pSelf->m_VoteCreator = pResult->m_ClientId; - - pSelf->m_pScore->RandomUnfinishedMap(pSelf->m_VoteCreator, Stars); + const int ClientId = pResult->m_ClientId == -1 ? pSelf->m_VoteCreator : pResult->m_ClientId; + const int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1; + pSelf->m_pScore->RandomUnfinishedMap(ClientId, Stars); } void CGameContext::ConRestart(IConsole::IResult *pResult, void *pUserData) @@ -4468,17 +4486,18 @@ IGameServer *CreateGameServer() { return new CGameContext; } void CGameContext::OnSetAuthed(int ClientId, int Level) { - if(m_apPlayers[ClientId]) + if(m_apPlayers[ClientId] && m_VoteCloseTime && Level != AUTHED_NO) { char aBuf[512], aIp[NETADDR_MAXSTRSIZE]; Server()->GetClientAddr(ClientId, aIp, sizeof(aIp)); str_format(aBuf, sizeof(aBuf), "ban %s %d Banned by vote", aIp, g_Config.m_SvVoteKickBantime); - if(!str_comp_nocase(m_aVoteCommand, aBuf) && Level > Server()->GetAuthedState(m_VoteCreator)) + if(!str_comp_nocase(m_aVoteCommand, aBuf) && (m_VoteCreator == -1 || Level > Server()->GetAuthedState(m_VoteCreator))) { m_VoteEnforce = CGameContext::VOTE_ENFORCE_NO_ADMIN; - Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "CGameContext", "Vote aborted by authorized login."); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "game", "Vote aborted by authorized login."); } } + if(m_TeeHistorianActive) { if(Level != AUTHED_NO) diff --git a/src/game/server/score.cpp b/src/game/server/score.cpp index 408ab626387..9c3e5a8508f 100644 --- a/src/game/server/score.cpp +++ b/src/game/server/score.cpp @@ -264,7 +264,7 @@ void CScore::RandomMap(int ClientId, int Stars) Tmp->m_Stars = Stars; str_copy(Tmp->m_aCurrentMap, Server()->GetMapName(), sizeof(Tmp->m_aCurrentMap)); str_copy(Tmp->m_aServerType, g_Config.m_SvServerType, sizeof(Tmp->m_aServerType)); - str_copy(Tmp->m_aRequestingPlayer, GameServer()->Server()->ClientName(ClientId), sizeof(Tmp->m_aRequestingPlayer)); + str_copy(Tmp->m_aRequestingPlayer, ClientId == -1 ? "nameless tee" : GameServer()->Server()->ClientName(ClientId), sizeof(Tmp->m_aRequestingPlayer)); m_pPool->Execute(CScoreWorker::RandomMap, std::move(Tmp), "random map"); } @@ -278,7 +278,7 @@ void CScore::RandomUnfinishedMap(int ClientId, int Stars) Tmp->m_Stars = Stars; str_copy(Tmp->m_aCurrentMap, Server()->GetMapName(), sizeof(Tmp->m_aCurrentMap)); str_copy(Tmp->m_aServerType, g_Config.m_SvServerType, sizeof(Tmp->m_aServerType)); - str_copy(Tmp->m_aRequestingPlayer, GameServer()->Server()->ClientName(ClientId), sizeof(Tmp->m_aRequestingPlayer)); + str_copy(Tmp->m_aRequestingPlayer, ClientId == -1 ? "nameless tee" : GameServer()->Server()->ClientName(ClientId), sizeof(Tmp->m_aRequestingPlayer)); m_pPool->Execute(CScoreWorker::RandomUnfinishedMap, std::move(Tmp), "random unfinished map"); } From fe88358bcca1066adc61aaa83c0d24c58ea50a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 7 Nov 2024 22:07:58 +0100 Subject: [PATCH 18/40] Improve entities/menu background map loading Fix loading screen rendering without progress bar for the entities background map during the initial loading. Remove the `bool RenderLoadingBar` parameter of the `CMenus::RenderLoading` function. The progress bar is now rendered as long as the loading total is set. Add the `CMenus::FinishLoading` function to reset the loading total at the end of the loading process. Remove the `bool RenderMenuBackgroundMap` parameter of the `CMenus::RenderLoading` function. The `RenderLoading` function will now check internally if the menu background map can be rendered. --- src/game/client/components/background.cpp | 6 +++++ src/game/client/components/background.h | 2 ++ src/game/client/components/maplayers.cpp | 16 ++++++------- src/game/client/components/maplayers.h | 4 +--- .../client/components/menu_background.cpp | 11 ++++++++- src/game/client/components/menu_background.h | 8 ++++--- src/game/client/components/menus.cpp | 23 +++++++++++++++---- src/game/client/components/menus.h | 3 ++- src/game/client/components/menus_demo.cpp | 2 +- src/game/client/components/menus_ingame.cpp | 2 +- .../components/menus_settings_assets.cpp | 2 +- src/game/client/components/race_demo.cpp | 2 +- src/game/client/gameclient.cpp | 11 +++++---- 13 files changed, 62 insertions(+), 30 deletions(-) diff --git a/src/game/client/components/background.cpp b/src/game/client/components/background.cpp index 73f658c61b2..cf1fb23fd3c 100644 --- a/src/game/client/components/background.cpp +++ b/src/game/client/components/background.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "background.h" @@ -33,6 +34,11 @@ CBackgroundEngineMap *CBackground::CreateBGMap() return new CBackgroundEngineMap; } +const char *CBackground::LoadingTitle() const +{ + return Localize("Loading background map"); +} + void CBackground::OnInit() { m_pBackgroundMap = CreateBGMap(); diff --git a/src/game/client/components/background.h b/src/game/client/components/background.h index 45e3066c467..df838ece744 100644 --- a/src/game/client/components/background.h +++ b/src/game/client/components/background.h @@ -32,6 +32,8 @@ class CBackground : public CMapLayers virtual CBackgroundEngineMap *CreateBGMap(); + const char *LoadingTitle() const override; + public: CBackground(int MapType = CMapLayers::TYPE_BACKGROUND_FORCE, bool OnlineOnly = true); virtual ~CBackground(); diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index 73927a87910..e6a77a514cb 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -41,6 +41,11 @@ CCamera *CMapLayers::GetCurCamera() return &m_pClient->m_Camera; } +const char *CMapLayers::LoadingTitle() const +{ + return GameClient()->DemoPlayer()->IsPlaying() ? Localize("Preparing demo playback") : Localize("Connected"); +} + void CMapLayers::EnvelopeEval(int TimeOffsetMillis, int Env, ColorRGBA &Result, size_t Channels, void *pUser) { CMapLayers *pThis = (CMapLayers *)pUser; @@ -276,15 +281,10 @@ void CMapLayers::OnMapLoad() if(!Graphics()->IsTileBufferingEnabled() && !Graphics()->IsQuadBufferingEnabled()) return; - const char *pConnectCaption = GameClient()->DemoPlayer()->IsPlaying() ? Localize("Preparing demo playback") : Localize("Connected"); - const char *pLoadMapContent = Localize("Uploading map data to GPU"); - - auto CurTime = time_get_nanoseconds(); + const char *pLoadingTitle = LoadingTitle(); + const char *pLoadingMessage = Localize("Uploading map data to GPU"); auto &&RenderLoading = [&]() { - if(CanRenderMenuBackground()) - GameClient()->m_Menus.RenderLoading(pConnectCaption, pLoadMapContent, 0, false); - else if(time_get_nanoseconds() - CurTime > 500ms) - GameClient()->m_Menus.RenderLoading(pConnectCaption, pLoadMapContent, 0, false, false); + GameClient()->m_Menus.RenderLoading(pLoadingTitle, pLoadingMessage, 0); }; //clear everything and destroy all buffers diff --git a/src/game/client/components/maplayers.h b/src/game/client/components/maplayers.h index e2d401f7c5d..4377a0f5509 100644 --- a/src/game/client/components/maplayers.h +++ b/src/game/client/components/maplayers.h @@ -126,12 +126,10 @@ class CMapLayers : public CComponent std::vector m_vpQuadLayerVisuals; virtual CCamera *GetCurCamera(); + virtual const char *LoadingTitle() const; void LayersOfGroupCount(CMapItemGroup *pGroup, int &TileLayerCount, int &QuadLayerCount, bool &PassedGameLayer); -protected: - virtual bool CanRenderMenuBackground() { return true; } - public: enum { diff --git a/src/game/client/components/menu_background.cpp b/src/game/client/components/menu_background.cpp index 9d57db9c0ed..a929625cf80 100644 --- a/src/game/client/components/menu_background.cpp +++ b/src/game/client/components/menu_background.cpp @@ -66,6 +66,7 @@ CMenuBackground::CMenuBackground() : m_MoveTime = 0.0f; m_IsInit = false; + m_Loading = false; } CBackgroundEngineMap *CMenuBackground::CreateBGMap() @@ -159,7 +160,7 @@ int CMenuBackground::ThemeScan(const char *pName, int IsDir, int DirType, void * if(time_get_nanoseconds() - pSelf->m_ThemeScanStartTime > 500ms) { - pSelf->GameClient()->m_Menus.RenderLoading(Localize("Loading menu themes"), "", 0, false); + pSelf->GameClient()->m_Menus.RenderLoading(Localize("Loading menu themes"), "", 0); } return 0; } @@ -183,6 +184,8 @@ void CMenuBackground::LoadMenuBackground(bool HasDayHint, bool HasNightHint) if(g_Config.m_ClMenuMap[0] != '\0') { + m_Loading = true; + const char *pMenuMap = g_Config.m_ClMenuMap; if(str_comp(pMenuMap, "auto") == 0) { @@ -287,6 +290,7 @@ void CMenuBackground::LoadMenuBackground(bool HasDayHint, bool HasNightHint) } } } + m_Loading = false; } } @@ -358,6 +362,11 @@ CCamera *CMenuBackground::GetCurCamera() return &m_Camera; } +const char *CMenuBackground::LoadingTitle() const +{ + return Localize("Loading background map"); +} + void CMenuBackground::ChangePosition(int PositionNumber) { if(PositionNumber != m_CurrentPosition) diff --git a/src/game/client/components/menu_background.h b/src/game/client/components/menu_background.h index a2f3791e6a0..a5ab02ebe47 100644 --- a/src/game/client/components/menu_background.h +++ b/src/game/client/components/menu_background.h @@ -33,9 +33,6 @@ class CMenuBackground : public CBackground { std::chrono::nanoseconds m_ThemeScanStartTime{0}; -protected: - bool CanRenderMenuBackground() override { return false; } - public: enum { @@ -78,6 +75,7 @@ class CMenuBackground : public CBackground PREDEFINED_THEMES_COUNT = 3, }; +private: CCamera m_Camera; CBackgroundEngineMap *CreateBGMap() override; @@ -91,6 +89,7 @@ class CMenuBackground : public CBackground float m_MoveTime; bool m_IsInit; + bool m_Loading; void ResetPositions(); @@ -99,6 +98,7 @@ class CMenuBackground : public CBackground std::vector m_vThemes; +public: CMenuBackground(); ~CMenuBackground() override {} virtual int Sizeof() const override { return sizeof(*this); } @@ -110,8 +110,10 @@ class CMenuBackground : public CBackground void LoadMenuBackground(bool HasDayHint = true, bool HasNightHint = true); bool Render(); + bool IsLoading() const { return m_Loading; } class CCamera *GetCurCamera() override; + const char *LoadingTitle() const override; void ChangePosition(int PositionNumber); diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 08ebaa67e40..c498bc8c11f 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -757,7 +757,7 @@ void CMenus::RenderMenubar(CUIRect Box, IClient::EClientState ClientState) } } -void CMenus::RenderLoading(const char *pCaption, const char *pContent, int IncreaseCounter, bool RenderLoadingBar, bool RenderMenuBackgroundMap) +void CMenus::RenderLoading(const char *pCaption, const char *pContent, int IncreaseCounter) { // TODO: not supported right now due to separate render thread @@ -770,18 +770,25 @@ void CMenus::RenderLoading(const char *pCaption, const char *pContent, int Incre if(Now - m_LoadingState.m_LastRender < std::chrono::nanoseconds(1s) / 60l) return; - m_LoadingState.m_LastRender = Now; - // need up date this here to get correct ms_GuiColor = color_cast(ColorHSLA(g_Config.m_UiColor, true)); Ui()->MapScreen(); - if(!RenderMenuBackgroundMap || !GameClient()->m_MenuBackground.Render()) + if(GameClient()->m_MenuBackground.IsLoading()) + { + // Avoid rendering while loading the menu background as this would otherwise + // cause the regular menu background to be rendered for a few frames while + // the menu background is not loaded yet. + return; + } + if(!GameClient()->m_MenuBackground.Render()) { RenderBackground(); } + m_LoadingState.m_LastRender = Now; + CUIRect Box; Ui()->Screen()->Margin(160.0f, &Box); @@ -798,7 +805,7 @@ void CMenus::RenderLoading(const char *pCaption, const char *pContent, int Incre Box.HSplitTop(24.0f, &Label, &Box); Ui()->DoLabel(&Label, pContent, 20.0f, TEXTALIGN_MC); - if(RenderLoadingBar) + if(m_LoadingState.m_Total > 0) { CUIRect ProgressBar; Box.HSplitBottom(30.0f, &Box, nullptr); @@ -812,6 +819,12 @@ void CMenus::RenderLoading(const char *pCaption, const char *pContent, int Incre Client()->UpdateAndSwap(); } +void CMenus::FinishLoading() +{ + m_LoadingState.m_Current = 0; + m_LoadingState.m_Total = 0; +} + void CMenus::RenderNews(CUIRect MainView) { GameClient()->m_MenuBackground.ChangePosition(CMenuBackground::POS_NEWS); diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index eed2e091d5a..8e6d255214a 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -661,7 +661,8 @@ class CMenus : public CComponent CMenus(); virtual int Sizeof() const override { return sizeof(*this); } - void RenderLoading(const char *pCaption, const char *pContent, int IncreaseCounter, bool RenderLoadingBar = true, bool RenderMenuBackgroundMap = true); + void RenderLoading(const char *pCaption, const char *pContent, int IncreaseCounter); + void FinishLoading(); bool IsInit() { return m_IsInit; } diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index dd671c14227..a7433725b92 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -920,7 +920,7 @@ int CMenus::DemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int Stora if(time_get_nanoseconds() - pSelf->m_DemoPopulateStartTime > 500ms) { - pSelf->RenderLoading(Localize("Loading demo files"), "", 0, false); + pSelf->RenderLoading(Localize("Loading demo files"), "", 0); } return 0; diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index beba28fc118..af55f596b76 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -1139,7 +1139,7 @@ int CMenus::GhostlistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int Stor if(time_get_nanoseconds() - pSelf->m_GhostPopulateStartTime > 500ms) { - pSelf->RenderLoading(Localize("Loading ghost files"), "", 0, false); + pSelf->RenderLoading(Localize("Loading ghost files"), "", 0); } return 0; diff --git a/src/game/client/components/menus_settings_assets.cpp b/src/game/client/components/menus_settings_assets.cpp index 7f0b7e3d557..c7412b889f3 100644 --- a/src/game/client/components/menus_settings_assets.cpp +++ b/src/game/client/components/menus_settings_assets.cpp @@ -377,7 +377,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView) User.m_pUser = this; User.m_LoadedFunc = [&]() { if(time_get_nanoseconds() - LoadStartTime > 500ms) - RenderLoading(Localize("Loading assets"), "", 0, false); + RenderLoading(Localize("Loading assets"), "", 0); }; if(s_CurCustomTab == ASSETS_TAB_ENTITIES) { diff --git a/src/game/client/components/race_demo.cpp b/src/game/client/components/race_demo.cpp index d1de5ef46a0..43c99019d63 100644 --- a/src/game/client/components/race_demo.cpp +++ b/src/game/client/components/race_demo.cpp @@ -233,7 +233,7 @@ int CRaceDemo::RaceDemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, in if(time_get_nanoseconds() - pRealUser->m_pThis->m_RaceDemosLoadStartTime > 500ms) { - pRealUser->m_pThis->GameClient()->m_Menus.RenderLoading(Localize("Loading race demo files"), "", 0, false); + pRealUser->m_pThis->GameClient()->m_Menus.RenderLoading(Localize("Loading race demo files"), "", 0); } return 0; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 2ebc4eb5ccb..cb779f6ea2a 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -304,7 +304,7 @@ void CGameClient::OnInit() dbg_assert(false, "Invalid callback loading detail"); dbg_break(); } - m_Menus.RenderLoading(pTitle, pMessage, 0, false); + m_Menus.RenderLoading(pTitle, pMessage, 0); }); m_pGraphics = Kernel()->RequestInterface(); @@ -428,6 +428,7 @@ void CGameClient::OnInit() pChecksum->m_aComponentsChecksum[i] = Size; } + m_Menus.FinishLoading(); log_trace("gameclient", "initialization finished after %.2fms", (time_get() - OnInitStart) * 1000.0f / (float)time_freq()); } @@ -553,14 +554,14 @@ void CGameClient::OnConnected() const char *pConnectCaption = DemoPlayer()->IsPlaying() ? Localize("Preparing demo playback") : Localize("Connected"); const char *pLoadMapContent = Localize("Initializing map logic"); // render loading before skip is calculated - m_Menus.RenderLoading(pConnectCaption, pLoadMapContent, 0, false); + m_Menus.RenderLoading(pConnectCaption, pLoadMapContent, 0); m_Layers.Init(Kernel()->RequestInterface(), false); m_Collision.Init(Layers()); m_GameWorld.m_Core.InitSwitchers(m_Collision.m_HighestSwitchNumber); m_RaceHelper.Init(this); // render loading before going through all components - m_Menus.RenderLoading(pConnectCaption, pLoadMapContent, 0, false); + m_Menus.RenderLoading(pConnectCaption, pLoadMapContent, 0); for(auto &pComponent : m_vpAll) { pComponent->OnMapLoad(); @@ -568,7 +569,7 @@ void CGameClient::OnConnected() } Client()->SetLoadingStateDetail(IClient::LOADING_STATE_DETAIL_GETTING_READY); - m_Menus.RenderLoading(pConnectCaption, Localize("Sending initial client info"), 0, false); + m_Menus.RenderLoading(pConnectCaption, Localize("Sending initial client info"), 0); // send the initial info SendInfo(true); @@ -3840,7 +3841,7 @@ void CGameClient::RefreshSkins() // if skin refreshing takes to long, swap to a loading screen if(time_get_nanoseconds() - SkinStartLoadTime > 500ms) { - m_Menus.RenderLoading(Localize("Loading skin files"), "", 0, false); + m_Menus.RenderLoading(Localize("Loading skin files"), "", 0); } }); From 60b2486165f2a99d771081fe4929ebe31b038830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 14 Dec 2024 16:19:48 +0100 Subject: [PATCH 19/40] Minor improvement of entities background map settings Reset the selected map when reloading the map list to fix the wrong selection being shown for one frame when navigating directories in the map selection popup. Increase scrolling speed for consistency with other listboxes (scroll 3 entries instead of 1). --- src/game/client/components/menus_settings.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index fa0e224d811..96110682029 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -3303,7 +3303,6 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView) static SPopupMenuId s_PopupMapPickerId; static CPopupMapPickerContext s_PopupMapPickerContext; s_PopupMapPickerContext.m_pMenus = this; - s_PopupMapPickerContext.MapListPopulate(); Ui()->DoPopupMenu(&s_PopupMapPickerId, Ui()->MouseX(), Ui()->MouseY(), 300.0f, 250.0f, &s_PopupMapPickerContext, PopupMapPicker); } @@ -3406,7 +3405,7 @@ CUi::EPopupMenuFunctionResult CMenus::PopupMapPicker(void *pContext, CUIRect Vie static CListBox s_ListBox; s_ListBox.SetActive(Active); - s_ListBox.DoStart(20.0f, pPopupContext->m_vMaps.size(), 1, 1, -1, &View, false); + s_ListBox.DoStart(20.0f, pPopupContext->m_vMaps.size(), 1, 3, -1, &View, false); int MapIndex = 0; for(auto &Map : pPopupContext->m_vMaps) @@ -3450,7 +3449,7 @@ CUi::EPopupMenuFunctionResult CMenus::PopupMapPicker(void *pContext, CUIRect Vie pPopupContext->m_Selection = NewSelected >= 0 ? NewSelected : -1; if(s_ListBox.WasItemSelected() || s_ListBox.WasItemActivated()) { - const CMapListItem SelectedItem = pPopupContext->m_vMaps[pPopupContext->m_Selection]; + const CMapListItem &SelectedItem = pPopupContext->m_vMaps[pPopupContext->m_Selection]; if(SelectedItem.m_IsDirectory) { @@ -3483,6 +3482,7 @@ void CMenus::CPopupMapPickerContext::MapListPopulate() str_format(aTemp, sizeof(aTemp), "maps/%s", m_aCurrentMapFolder); m_pMenus->Storage()->ListDirectoryInfo(IStorage::TYPE_ALL, aTemp, MapListFetchCallback, this); std::stable_sort(m_vMaps.begin(), m_vMaps.end(), CompareFilenameAscending); + m_Selection = -1; } int CMenus::CPopupMapPickerContext::MapListFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser) From f8552d0d8caf063f9af9b732a5879400fba0f295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 14 Dec 2024 19:47:00 +0100 Subject: [PATCH 20/40] Remove obsolete `.gitignore` entries for bam --- .gitignore | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitignore b/.gitignore index 3c84340848a..b199c00f845 100644 --- a/.gitignore +++ b/.gitignore @@ -108,11 +108,6 @@ tags # don't ignore this, it's used for the vscode workspace that ddnet provides !ddnet-cmake-tools-kits.json -# bam ignores -/.bam -/config.lua -/objs - *.a *.cmd *.csv From 9c07d4b795b74af4dae98786c68023cc4c652140 Mon Sep 17 00:00:00 2001 From: ASKLL Date: Sun, 15 Dec 2024 21:40:02 +0800 Subject: [PATCH 21/40] display properly on a wider resolution Makes winter_night menu theme background can display properly on a wider resolution --- data/themes/winter_night.map | Bin 7109 -> 6956 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/themes/winter_night.map b/data/themes/winter_night.map index b23a089d0da24e7c1ce18a6cfc737846387189f2..89061c0ba468fabc507a21be5cc86a863dcfa34b 100644 GIT binary patch delta 137 zcmX?VzQ#<^#WBQ@g@J)VMw)@)4;upmJCH2~#9To9hLwRqVWMLA#0e3T7-cvQ0)>77 zvD;=uOV}G6lPx~~{C`LH{(s{cp9~r@73!;**qJ-{mq|+;n8E4M$*|Ez;D7po lC(NNnKNt!n`RrsiEZDg4hrK{UOM(f*oDkWwo6|Vrg#p|mD>DE9 delta 283 zcmZ2ucGO(a#WBQ@g@J)#t26_{6m|v%P9R$bh`E3`gpGkgaiU_l;J^P23}Qe5Rv`Wl zWJ^yhj2C+YGU4Ti!Kgcy@575<%XILW>$ Date: Fri, 6 Dec 2024 18:04:28 +0100 Subject: [PATCH 22/40] Support running local server on Android Compile the server as a separate library for Android same as the client. Use a foreground service to run the native `main` function of the server using Java Native Interface. Directly using JNI avoids the use of SDL as a wrapper for the server. The server service must run in a different process because it needs to be terminated to correctly restart the server. Otherwise it crashes because global variables are not initialized again when it is restarted. This also prevents simply running the server in another thread of the client process. A toast error message is shown on non-zero return values of server's `main` function. A foreground service is used so the server can keep running while the client activity is in the background. Add detailed description why the service uses `android:foregroundServiceType="specialUse"` in the manifest as this should convince the reviewer that the use case is valid. None of the other foreground service types cover hosting local game servers or related use cases. The required permissions for using a foreground service with special use case are added as well as the permission to post notifications. Showing a notification is necessary for foreground services to stay alive and it also allows showing the server status and controlling the server. In particular, an action to directly stop the server is shown. Another action to run commands based on user input in the notification is provided, which allows setting the initial rcon password. The server will also be stopped automatically if the client is quit or if the server notification is deleted (which is possible on newer Android versions that do not allow ongoing notifications anymore). Rename `NativeMain.java` to `ClientActivity.java` to differentiate the file better from the server class. --- CMakeLists.txt | 40 ++- cmake/FindAndroid.cmake | 14 +- scripts/android/cmake_android.sh | 6 +- scripts/android/files/AndroidManifest.xml | 22 +- scripts/android/files/build.gradle | 3 + scripts/android/files/build.sh | 3 +- .../java/org/ddnet/client/ClientActivity.java | 133 ++++++++ .../java/org/ddnet/client/NativeMain.java | 65 ---- .../java/org/ddnet/client/ServerService.java | 316 ++++++++++++++++++ scripts/android/files/res/values/strings.xml | 8 + src/android/android_main.cpp | 62 +++- src/android/android_main.h | 45 +++ src/engine/server/main.cpp | 60 ++++ src/engine/server/server.cpp | 12 + src/game/client/components/menus.cpp | 2 - src/game/client/components/menus.h | 6 +- src/game/client/components/menus_start.cpp | 75 +++-- 17 files changed, 761 insertions(+), 111 deletions(-) create mode 100644 scripts/android/files/java/org/ddnet/client/ClientActivity.java delete mode 100644 scripts/android/files/java/org/ddnet/client/NativeMain.java create mode 100644 scripts/android/files/java/org/ddnet/client/ServerService.java diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d4f3cca95b..9b2f017c285 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -689,8 +689,8 @@ endif() if(CLIENT AND NOT(SDL2_FOUND)) message(SEND_ERROR "You must install SDL2 to compile the ${CMAKE_PROJECT_NAME} client") endif() -if(TARGET_OS STREQUAL "android" AND CLIENT AND NOT(CRYPTO_FOUND)) - message(SEND_ERROR "You must install OpenSSL to compile the ${CMAKE_PROJECT_NAME} client") +if(TARGET_OS STREQUAL "android" AND NOT(CRYPTO_FOUND)) + message(SEND_ERROR "You must install OpenSSL to compile ${CMAKE_PROJECT_NAME}") endif() if(NOT(GTEST_FOUND)) if(DOWNLOAD_GTEST) @@ -741,7 +741,7 @@ elseif(TARGET_OS STREQUAL "android") src/android/android_main.cpp ) set(PLATFORM_LIBS ${TW_ANDROID_LIBS}) - set(PLATFORM_CLIENT_LIBS ${PLATFORM_LIBS}) + set(PLATFORM_CLIENT_LIBS ${TW_ANDROID_LIBS_CLIENT}) set(PLATFORM_CLIENT_INCLUDE_DIRS) else() find_package(Notify) @@ -2782,14 +2782,25 @@ if(SERVER) ) # Target - add_executable(game-server - ${DEPS} - ${SERVER_SRC} - ${SERVER_ICON} - $ - $ - $ - ) + if(TARGET_OS STREQUAL "android") + add_library(game-server SHARED + ${DEPS} + ${SERVER_SRC} + ${SERVER_ICON} + $ + $ + $ + ) + else() + add_executable(game-server + ${DEPS} + ${SERVER_SRC} + ${SERVER_ICON} + $ + $ + $ + ) + endif() set_property(TARGET game-server PROPERTY OUTPUT_NAME ${SERVER_EXECUTABLE} ) @@ -3600,6 +3611,13 @@ foreach(target ${TARGETS_OWN}) if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") target_compile_definitions(${target} PRIVATE CONF_WEBASM) endif() + if(TARGET_OS STREQUAL "android") + if(ANDROID_PACKAGE_NAME) + target_compile_definitions(${target} PRIVATE ANDROID_PACKAGE_NAME=${ANDROID_PACKAGE_NAME}) + else() + message(FATAL_ERROR "ANDROID_PACKAGE_NAME must define the package name when compiling for Android (using underscores instead of dots, e.g. org_example_app)") + endif() + endif() endforeach() foreach(target ${TARGETS_DEP}) diff --git a/cmake/FindAndroid.cmake b/cmake/FindAndroid.cmake index 537ed1f5b03..a42a0a1f5ae 100644 --- a/cmake/FindAndroid.cmake +++ b/cmake/FindAndroid.cmake @@ -27,4 +27,16 @@ FIND_LIBRARY(ANDROID_LIBRARY_OPENSLES OpenSLES ) -set(TW_ANDROID_LIBS ${ANDROID_LIBRARY_EGL} ${ANDROID_LIBRARY_GLES1} ${ANDROID_LIBRARY_GLES2} ${ANDROID_LIBRARY_GLES3} ${ANDROID_LIBRARY_LOG} ${ANDROID_LIBRARY_ANDROID} ${ANDROID_LIBRARY_OPENSLES}) +set(TW_ANDROID_LIBS + ${ANDROID_LIBRARY_LOG} + ${ANDROID_LIBRARY_ANDROID} +) + +set(TW_ANDROID_LIBS_CLIENT + ${TW_ANDROID_LIBS} + ${ANDROID_LIBRARY_EGL} + ${ANDROID_LIBRARY_GLES1} + ${ANDROID_LIBRARY_GLES2} + ${ANDROID_LIBRARY_GLES3} + ${ANDROID_LIBRARY_OPENSLES} +) diff --git a/scripts/android/cmake_android.sh b/scripts/android/cmake_android.sh index f2720d49971..a8b7ad0ceac 100755 --- a/scripts/android/cmake_android.sh +++ b/scripts/android/cmake_android.sh @@ -153,6 +153,7 @@ function build_for_type() { -DANDROID_NDK="$ANDROID_NDK_HOME" \ -DANDROID_ABI="${2}" \ -DANDROID_ARM_NEON=TRUE \ + -DANDROID_PACKAGE_NAME="${PACKAGE_NAME//./_}" \ -DCMAKE_ANDROID_NDK="$ANDROID_NDK_HOME" \ -DCMAKE_SYSTEM_NAME=Android \ -DCMAKE_SYSTEM_VERSION="$ANDROID_API_LEVEL" \ @@ -160,7 +161,7 @@ function build_for_type() { -DCARGO_NDK_TARGET="${3}" \ -DCARGO_NDK_API="$ANDROID_API_LEVEL" \ -B"${BUILD_FOLDER}/$ANDROID_SUB_BUILD_DIR/$1" \ - -DSERVER=OFF \ + -DSERVER=ON \ -DTOOLS=OFF \ -DDEV=TRUE \ -DCMAKE_CROSSCOMPILING=ON \ @@ -170,7 +171,7 @@ function build_for_type() { cd "${BUILD_FOLDER}/$ANDROID_SUB_BUILD_DIR/$1" || exit 1 # We want word splitting # shellcheck disable=SC2086 - cmake --build . --target game-client $BUILD_FLAGS + cmake --build . --target game-client game-server $BUILD_FLAGS ) } @@ -228,6 +229,7 @@ log_info "Copying libraries..." function copy_libs() { mkdir -p "lib/$2" cp "$ANDROID_SUB_BUILD_DIR/$1/libDDNet.so" "lib/$2" || exit 1 + cp "$ANDROID_SUB_BUILD_DIR/$1/libDDNet-Server.so" "lib/$2" || exit 1 } if [[ "${ANDROID_BUILD}" == "arm" || "${ANDROID_BUILD}" == "all" ]]; then diff --git a/scripts/android/files/AndroidManifest.xml b/scripts/android/files/AndroidManifest.xml index 3106bd9a394..e4ac45733a3 100644 --- a/scripts/android/files/AndroidManifest.xml +++ b/scripts/android/files/AndroidManifest.xml @@ -41,21 +41,27 @@ + + + + + + + + + diff --git a/scripts/android/files/build.gradle b/scripts/android/files/build.gradle index cc1a5390423..5789fe04124 100644 --- a/scripts/android/files/build.gradle +++ b/scripts/android/files/build.gradle @@ -61,6 +61,9 @@ android { lintOptions { abortOnError false } + dependencies { + implementation 'androidx.core:core:1.13.1' + } } allprojects { diff --git a/scripts/android/files/build.sh b/scripts/android/files/build.sh index 7958e7f67b1..7d0859a495a 100644 --- a/scripts/android/files/build.sh +++ b/scripts/android/files/build.sh @@ -59,7 +59,8 @@ if [ "${APK_PACKAGE_FOLDER}" != "org/ddnet/client" ]; then mv src/main/java/org/ddnet/client src/main/java/"${APK_PACKAGE_FOLDER}" fi -sed -i "s/org.ddnet.client/${APK_PACKAGE_NAME}/g" src/main/java/"${APK_PACKAGE_FOLDER}"/NativeMain.java +sed -i "s/org.ddnet.client/${APK_PACKAGE_NAME}/g" src/main/java/"${APK_PACKAGE_FOLDER}"/ClientActivity.java +sed -i "s/org.ddnet.client/${APK_PACKAGE_NAME}/g" src/main/java/"${APK_PACKAGE_FOLDER}"/ServerService.java sed -i "s/org.ddnet.client/${APK_PACKAGE_NAME}/g" proguard-rules.pro # disable hid manager for now diff --git a/scripts/android/files/java/org/ddnet/client/ClientActivity.java b/scripts/android/files/java/org/ddnet/client/ClientActivity.java new file mode 100644 index 00000000000..e51d9c1b7f4 --- /dev/null +++ b/scripts/android/files/java/org/ddnet/client/ClientActivity.java @@ -0,0 +1,133 @@ +package org.ddnet.client; + +import android.app.NativeActivity; +import android.content.*; +import android.content.pm.ActivityInfo; +import android.os.*; + +import androidx.core.content.ContextCompat; + +import org.libsdl.app.SDLActivity; + +public class ClientActivity extends SDLActivity { + + private static final int COMMAND_RESTART_APP = SDLActivity.COMMAND_USER + 1; + + private String[] launchArguments = new String[0]; + + private final Object serverServiceMonitor = new Object(); + private Messenger serverServiceMessenger = null; + private final ServiceConnection serverServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized(serverServiceMonitor) { + serverServiceMessenger = new Messenger(service); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + synchronized(serverServiceMonitor) { + serverServiceMessenger = null; + } + } + }; + + @Override + protected String[] getLibraries() { + return new String[] { + "DDNet", + }; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + + Intent intent = getIntent(); + if(intent != null) { + String gfxBackend = intent.getStringExtra("gfx-backend"); + if(gfxBackend != null) { + if(gfxBackend.equals("Vulkan")) { + launchArguments = new String[] {"gfx_backend Vulkan"}; + } else if(gfxBackend.equals("GLES")) { + launchArguments = new String[] {"gfx_backend GLES"}; + } + } + } + + super.onCreate(savedInstanceState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + synchronized(serverServiceMonitor) { + if(serverServiceMessenger != null) { + unbindService(serverServiceConnection); + } + } + } + + @Override + protected String[] getArguments() { + return launchArguments; + } + + @Override + protected boolean onUnhandledMessage(int command, Object param) { + switch(command) { + case COMMAND_RESTART_APP: + restartApp(); + return true; + } + return false; + } + + private void restartApp() { + Intent restartIntent = + Intent.makeRestartActivityTask( + getPackageManager().getLaunchIntentForPackage( + getPackageName() + ).getComponent() + ); + restartIntent.setPackage(getPackageName()); + startActivity(restartIntent); + } + + // Called from native code, see android_main.cpp + public void startServer() { + synchronized(serverServiceMonitor) { + if(serverServiceMessenger != null) { + return; + } + Intent startIntent = new Intent(this, ServerService.class); + ContextCompat.startForegroundService(this, startIntent); + bindService(startIntent, serverServiceConnection, 0); + } + } + + // Called from native code, see android_main.cpp + public void executeCommand(String command) { + synchronized(serverServiceMonitor) { + if(serverServiceMessenger == null) { + return; + } + try { + Message message = Message.obtain(null, ServerService.MESSAGE_CODE_EXECUTE_COMMAND, 0, 0); + message.getData().putString(ServerService.MESSAGE_EXTRA_COMMAND, command); + serverServiceMessenger.send(message); + } catch (RemoteException e) { + // Connection broken + unbindService(serverServiceConnection); + } + } + } + + // Called from native code, see android_main.cpp + public boolean isServerRunning() { + synchronized(serverServiceMonitor) { + return serverServiceMessenger != null; + } + } +} diff --git a/scripts/android/files/java/org/ddnet/client/NativeMain.java b/scripts/android/files/java/org/ddnet/client/NativeMain.java deleted file mode 100644 index e56d4cebca8..00000000000 --- a/scripts/android/files/java/org/ddnet/client/NativeMain.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.ddnet.client; - -import android.app.NativeActivity; -import org.libsdl.app.SDLActivity; -import android.os.Bundle; -import android.content.Intent; -import android.content.pm.ActivityInfo; - -public class NativeMain extends SDLActivity { - - private static final int COMMAND_RESTART_APP = SDLActivity.COMMAND_USER + 1; - - private String[] launchArguments = new String[0]; - - @Override - protected String[] getLibraries() { - return new String[] { - "DDNet", - }; - } - - @Override - public void onCreate(Bundle SavedInstanceState) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - - Intent intent = getIntent(); - if(intent != null) { - String gfxBackend = intent.getStringExtra("gfx-backend"); - if(gfxBackend != null) { - if(gfxBackend.equals("Vulkan")) { - launchArguments = new String[] {"gfx_backend Vulkan"}; - } else if(gfxBackend.equals("GLES")) { - launchArguments = new String[] {"gfx_backend GLES"}; - } - } - } - - super.onCreate(SavedInstanceState); - } - - @Override - protected String[] getArguments() { - return launchArguments; - } - - @Override - protected boolean onUnhandledMessage(int command, Object param) { - if(command == COMMAND_RESTART_APP) { - restartApp(); - return true; - } - return false; - } - - private void restartApp() { - Intent restartIntent = - Intent.makeRestartActivityTask( - getPackageManager().getLaunchIntentForPackage( - getPackageName() - ).getComponent() - ); - restartIntent.setPackage(getPackageName()); - startActivity(restartIntent); - } -} diff --git a/scripts/android/files/java/org/ddnet/client/ServerService.java b/scripts/android/files/java/org/ddnet/client/ServerService.java new file mode 100644 index 00000000000..ba297bb786f --- /dev/null +++ b/scripts/android/files/java/org/ddnet/client/ServerService.java @@ -0,0 +1,316 @@ +package org.ddnet.client; + +import java.io.File; + +import androidx.core.app.RemoteInput; +import androidx.core.app.NotificationCompat; + +import android.app.*; +import android.content.*; +import android.content.pm.ServiceInfo; +import android.os.*; +import android.util.*; +import android.widget.Toast; + +public class ServerService extends Service { + + private static final String NOTIFICATION_CHANNEL_ID = "LOCAL_SERVER_CHANNEL_ID"; + private static final int NOTIFICATION_ID = 1; + + public static final int MESSAGE_CODE_EXECUTE_COMMAND = 1; + public static final String MESSAGE_EXTRA_COMMAND = "command"; + + public static final String INTENT_ACTION_EXECUTE = "execute"; + public static final String INTENT_EXTRA_COMMAND = "command"; + private static final String KEY_EXECUTE_TEXT_REPLY = "execute-command-reply"; + + static { + System.loadLibrary("DDNet-Server"); + } + + private class IncomingHandler extends Handler { + + IncomingHandler(Context context) { + super(context.getMainLooper()); + } + + @Override + public void handleMessage(Message message) { + switch(message.what) { + case MESSAGE_CODE_EXECUTE_COMMAND: + String command = message.getData().getString(MESSAGE_EXTRA_COMMAND); + if(command != null) { + executeCommand(command); + } + break; + default: + super.handleMessage(message); + break; + } + } + } + + private Messenger messenger; + private NotificationManager notificationManager; + private NativeServerThread thread; + private boolean stopping = false; + + @Override + public void onCreate() { + super.onCreate(); + + notificationManager = getSystemService(NotificationManager.class); + + createNotificationChannel(); + + Notification notification = createRunningNotification(); + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + startForeground( + NOTIFICATION_ID, + notification, + ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST + ); + } else { + startForeground( + NOTIFICATION_ID, + notification + ); + } + + thread = new NativeServerThread(this); + thread.start(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if(intent != null) { + String action = intent.getAction(); + if(INTENT_ACTION_EXECUTE.equals(action)) { + Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); + if(remoteInput != null) { + CharSequence remoteCommand = remoteInput.getCharSequence(KEY_EXECUTE_TEXT_REPLY); + if(remoteCommand != null) { + executeCommand(remoteCommand.toString()); + } + if(!stopping) { + // Need to send the notification again to acknowledge that we got the remote input, + // otherwise the remote input will not be completed. + notificationManager.notify(NOTIFICATION_ID, createRunningNotification()); + } + } else { + String command = intent.getStringExtra(INTENT_EXTRA_COMMAND); + if(command != null) { + executeCommand(command); + } + } + } + } + return START_NOT_STICKY; + } + + public void onDestroy() { + super.onDestroy(); + executeCommand("shutdown"); + stopForeground(0); + if(thread != null) { + try { + thread.join(2500); + if(thread.isAlive()) { + // Native server is not reacting to the shutdown command, force stop. + System.exit(0); + } + } catch (InterruptedException e) { + } + thread = null; + } + } + + @Override + public IBinder onBind(Intent intent) { + messenger = new Messenger(new IncomingHandler(this)); + return messenger.getBinder(); + } + + @Override + public boolean onUnbind(Intent intent) { + // Ensure server is stopped when the client is unbound from this service, + // which covers the case where the client activity is killed while in the + // background, which is otherwise not possible to detect. + executeCommand("shutdown"); + return false; + } + + private void createNotificationChannel() { + if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return; + } + NotificationChannel channel = new NotificationChannel( + NOTIFICATION_CHANNEL_ID, + getString(R.string.server_name), + NotificationManager.IMPORTANCE_DEFAULT + ); + channel.setDescription(getString(R.string.server_notification_channel_description)); + notificationManager.createNotificationChannel(channel); + } + + private Notification createRunningNotification() { + Intent activityIntent = new Intent(this, ClientActivity.class); + + PendingIntent activityActionIntent = PendingIntent.getActivity( + this, + 0, // request code (unused) + activityIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE + ); + + Intent stopIntent = new Intent(this, ServerService.class); + stopIntent.setAction(INTENT_ACTION_EXECUTE); + stopIntent.putExtra(INTENT_EXTRA_COMMAND, "shutdown"); + + PendingIntent stopActionIntent = PendingIntent.getService( + this, + 0, // request code (unused) + stopIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE + ); + + NotificationCompat.Action stopAction = + new NotificationCompat.Action.Builder( + android.R.drawable.ic_menu_view, + getString(R.string.server_notification_action_stop), + stopActionIntent + ).setAuthenticationRequired(true) // not allowed from lock screen + .build(); + + Intent executeCommandIntent = new Intent(this, ServerService.class); + executeCommandIntent.setAction(INTENT_ACTION_EXECUTE); + + PendingIntent executeCommandActionIntent = PendingIntent.getService( + this, + 0, // request code (unused) + executeCommandIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE + ); + + RemoteInput remoteInput = + new RemoteInput.Builder(KEY_EXECUTE_TEXT_REPLY) + .setLabel(getString(R.string.server_notification_action_run_command)) + .build(); + + NotificationCompat.Action executeAction = + new NotificationCompat.Action.Builder( + android.R.drawable.ic_menu_view, + getString(R.string.server_notification_action_run_command), + executeCommandActionIntent + ).setAuthenticationRequired(true) // not allowed from lock screen + .addRemoteInput(remoteInput) + .build(); + + // TODO: Update the notification text (setContentText) while server is running: + // show our LAN IP, show current player count, show last executed command + return new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) + .setOngoing(true) + .setAutoCancel(false) + .setContentTitle(getString(R.string.server_notification_description_default)) + .setSmallIcon(R.mipmap.ic_launcher) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setAllowSystemGeneratedContextualActions(false) + .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE) + .setContentIntent(activityActionIntent) // clicking on the notification opens the activity + .setDeleteIntent(stopActionIntent) // deleting the notification will also stop the server + .addAction(stopAction) + .addAction(executeAction) + .build(); + } + + private Notification createStoppingNotification() { + return new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) + .setOngoing(true) + .setContentTitle(getString(R.string.server_notification_description_stopping)) + .setSmallIcon(R.mipmap.ic_launcher) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setAllowSystemGeneratedContextualActions(false) + .build(); + } + + private void executeCommand(String command) { + if(thread == null) { + return; + } + // Detect simple case where the server is being stopped to update the notification + if("shutdown".equalsIgnoreCase(command)) { + if(stopping) { + return; + } + stopping = true; + notificationManager.notify(NOTIFICATION_ID, createStoppingNotification()); + } + NativeServer.executeCommand(command); + } +} + +/** + * Thread that runs the native server's main function. This thread is necessary so + * we don't block the service's main thread which is responsible for handling the + * service's lifecycle. + */ +class NativeServerThread extends Thread { + + private final Context applicationContext; + + public NativeServerThread(Context context) { + this.applicationContext = context.getApplicationContext(); + } + + @Override + public void run() { + File workingDirectory = applicationContext.getExternalFilesDir(null); + if(workingDirectory == null) { + new Handler(applicationContext.getMainLooper()).post(() -> { + Toast.makeText(applicationContext, R.string.server_error_external_files_inaccessible, Toast.LENGTH_LONG).show(); + terminateProcess(); + }); + return; + } + + int Result = NativeServer.runServer(workingDirectory.getAbsolutePath()); + new Handler(applicationContext.getMainLooper()).post(() -> { + if(Result != 0) { + Toast.makeText(applicationContext, applicationContext.getString(R.string.server_error_exit_code, Result), Toast.LENGTH_LONG).show(); + } + terminateProcess(); + }); + } + + private static void terminateProcess() { + // Forcefully terminate the entire process, to ensure that static variables will + // be initialized correctly when the server is started again after being stopped. + System.exit(0); + } +} + +/** + * Wrapper for functions that are implemented using JNI in engine/server/main.cpp. + */ +class NativeServer { + + private NativeServer() { + throw new AssertionError(); + } + + /** + * Runs the native server main function in the current thread and returns the + * exit code on completion. + * + * @param workingDirectory The working directory for the server, which must be the + * external storage directory of the app and already contains all data files. + */ + public static native int runServer(String workingDirectory); + + /** + * Adds a command to the execution queue of the native server. + * + * @param command The command to add to the queue. + */ + public static native void executeCommand(String command); +} diff --git a/scripts/android/files/res/values/strings.xml b/scripts/android/files/res/values/strings.xml index c9b6483ce76..f8021c6d644 100644 --- a/scripts/android/files/res/values/strings.xml +++ b/scripts/android/files/res/values/strings.xml @@ -3,4 +3,12 @@ DDNet Play (Vulkan) Play (OpenGL ES) + DDNet-Server + DDNet-Server is running… + DDNet-Server is stopping… + Stop + Run command + Notification to control the local DDNet-Server while it\'s running. + Error starting DDNet-Server: could not access external files directory. + DDNet-Server stopped with error code \'%1$d\'. diff --git a/src/android/android_main.cpp b/src/android/android_main.cpp index e60ec49765b..37790e689ce 100644 --- a/src/android/android_main.cpp +++ b/src/android/android_main.cpp @@ -1,7 +1,5 @@ #include "android_main.h" -#include - #include #include #include @@ -11,6 +9,10 @@ #include #include +#include + +#include + static bool UnpackAsset(const char *pFilename) { char aAssetFilename[IO_MAX_PATH_LENGTH]; @@ -226,7 +228,7 @@ const char *InitAndroid() return nullptr; } -// See NativeMain.java +// See ClientActivity.java constexpr uint32_t COMMAND_USER = 0x8000; constexpr uint32_t COMMAND_RESTART_APP = COMMAND_USER + 1; @@ -234,3 +236,57 @@ void RestartAndroidApp() { SDL_AndroidSendMessage(COMMAND_RESTART_APP, 0); } + +bool StartAndroidServer() +{ + // We need the notification-permission to show a notification for the foreground service. + // We use SDL for this instead of doing it on the Java side because this function blocks + // until the user made a choice, which is easier to handle. + if(!SDL_AndroidRequestPermission("android.permission.POST_NOTIFICATIONS")) + { + return false; + } + + JNIEnv *pEnv = static_cast(SDL_AndroidGetJNIEnv()); + jobject Activity = (jobject)SDL_AndroidGetActivity(); + jclass ActivityClass = pEnv->GetObjectClass(Activity); + + jmethodID MethodId = pEnv->GetMethodID(ActivityClass, "startServer", "()V"); + pEnv->CallVoidMethod(Activity, MethodId); + + pEnv->DeleteLocalRef(Activity); + pEnv->DeleteLocalRef(ActivityClass); + + return true; +} + +void ExecuteAndroidServerCommand(const char *pCommand) +{ + JNIEnv *pEnv = static_cast(SDL_AndroidGetJNIEnv()); + jobject Activity = (jobject)SDL_AndroidGetActivity(); + jclass ActivityClass = pEnv->GetObjectClass(Activity); + + jstring Command = pEnv->NewStringUTF(pCommand); + + jmethodID MethodId = pEnv->GetMethodID(ActivityClass, "executeCommand", "(Ljava/lang/String;)V"); + pEnv->CallVoidMethod(Activity, MethodId, Command); + + pEnv->DeleteLocalRef(Command); + pEnv->DeleteLocalRef(Activity); + pEnv->DeleteLocalRef(ActivityClass); +} + +bool IsAndroidServerRunning() +{ + JNIEnv *pEnv = static_cast(SDL_AndroidGetJNIEnv()); + jobject Activity = (jobject)SDL_AndroidGetActivity(); + jclass ActivityClass = pEnv->GetObjectClass(Activity); + + jmethodID MethodId = pEnv->GetMethodID(ActivityClass, "isServerRunning", "()Z"); + const bool Result = pEnv->CallBooleanMethod(Activity, MethodId); + + pEnv->DeleteLocalRef(Activity); + pEnv->DeleteLocalRef(ActivityClass); + + return Result; +} diff --git a/src/android/android_main.h b/src/android/android_main.h index c7e3a3e88d4..02fcf8f18c0 100644 --- a/src/android/android_main.h +++ b/src/android/android_main.h @@ -6,10 +6,23 @@ #error "This header should only be included when compiling for Android" #endif +/** + * @defgroup Android + * + * Android-specific functions to interact with the ClientActivity. + * + * Important note: These functions may only be called from the main native thread + * which is created by the SDLActivity (super class of ClientActivity), otherwise + * JNI calls are not possible because the JNI environment is not attached to that + * thread. See https://developer.android.com/training/articles/perf-jni#threads + */ + /** * Initializes the Android storage. Must be called on Android-systems * before using any of the I/O and storage functions. * + * @ingroup Android + * * This will change the current working directory to the app specific external * storage location and unpack the assets from the APK file to the `data` folder. * The folder `user` is created in the external storage to store the user data. @@ -23,9 +36,41 @@ const char *InitAndroid(); /** * Sends an intent to the Android system to restart the app. * + * @ingroup Android + * * This will restart the main activity in a new task. The current process * must immediately terminate after this function is called. */ void RestartAndroidApp(); +/** + * Starts the local server as an Android service. + * + * @ingroup Android + * + * This will request the notification-permission as it is required for + * foreground services to show a notification. + * + * @return `true` on success, `false` on error. + */ +bool StartAndroidServer(); + +/** + * Adds a command to the execution queue of the local server. + * + * @ingroup Android + * + * @param pCommand The command to enqueue. + */ +void ExecuteAndroidServerCommand(const char *pCommand); + +/** + * Returns whether the local server and its Android service are running. + * + * @ingroup Android + * + * @return `true` if the server is running, `false` if the server is stopped. + */ +bool IsAndroidServerRunning(); + #endif // ANDROID_ANDROID_MAIN_H diff --git a/src/engine/server/main.cpp b/src/engine/server/main.cpp index 5904b3245c9..1e592923458 100644 --- a/src/engine/server/main.cpp +++ b/src/engine/server/main.cpp @@ -17,10 +17,13 @@ #include +#include #include #if defined(CONF_FAMILY_WINDOWS) #include +#elif defined(CONF_PLATFORM_ANDROID) +#include #endif #include @@ -46,6 +49,8 @@ int main(int argc, const char **argv) const int64_t MainStart = time_get(); CCmdlineFix CmdlineFix(&argc, &argv); + +#if !defined(CONF_PLATFORM_ANDROID) bool Silent = false; for(int i = 1; i < argc; i++) @@ -59,6 +64,7 @@ int main(int argc, const char **argv) break; } } +#endif #if defined(CONF_FAMILY_WINDOWS) CWindowsComLifecycle WindowsComLifecycle(false); @@ -206,3 +212,57 @@ int main(int argc, const char **argv) return Ret; } + +#if defined(CONF_PLATFORM_ANDROID) +#if !defined(ANDROID_PACKAGE_NAME) +#error "ANDROID_PACKAGE_NAME must define the package name when compiling for Android (using underscores instead of dots, e.g. org_example_app)" +#endif +// Helpers to force macro expansion else the ANDROID_PACKAGE_NAME macro is not expanded +#define EXPAND_MACRO(x) x +#define JNI_MAKE_NAME(PACKAGE, CLASS, FUNCTION) Java_##PACKAGE##_##CLASS##_##FUNCTION +#define JNI_EXPORTED_FUNCTION(PACKAGE, CLASS, FUNCTION, RETURN_TYPE, ...) \ + extern "C" JNIEXPORT RETURN_TYPE JNICALL EXPAND_MACRO(JNI_MAKE_NAME(PACKAGE, CLASS, FUNCTION))(__VA_ARGS__) + +std::mutex AndroidNativeMutex; +std::vector vAndroidCommandQueue; + +std::vector FetchAndroidServerCommandQueue() +{ + std::vector vResult; + { + const std::unique_lock Lock(AndroidNativeMutex); + vResult.swap(vAndroidCommandQueue); + } + return vResult; +} + +JNI_EXPORTED_FUNCTION(ANDROID_PACKAGE_NAME, NativeServer, runServer, jint, JNIEnv *pEnv, jobject Object, jstring WorkingDirectory) +{ + // Set working directory to external storage location. This is not possible + // in Java so we pass the intended working directory to the native code. + const char *pWorkingDirectory = pEnv->GetStringUTFChars(WorkingDirectory, nullptr); + const bool WorkingDirectoryError = fs_chdir(pWorkingDirectory) != 0; + pEnv->ReleaseStringUTFChars(WorkingDirectory, pWorkingDirectory); + if(WorkingDirectoryError) + { + return -1001; + } + + const char *apArgs[] = {GAME_NAME}; + return main(std::size(apArgs), apArgs); +} + +JNI_EXPORTED_FUNCTION(ANDROID_PACKAGE_NAME, NativeServer, executeCommand, void, JNIEnv *pEnv, jobject Object, jstring Command) +{ + const char *pCommand = pEnv->GetStringUTFChars(Command, nullptr); + { + const std::unique_lock Lock(AndroidNativeMutex); + vAndroidCommandQueue.emplace_back(pCommand); + } + pEnv->ReleaseStringUTFChars(Command, pCommand); +} + +#undef EXPAND_MACRO +#undef JNI_MAKE_NAME +#undef JNI_EXPORTED_FUNCTION +#endif diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index b2c0e93b67e..242639f85b3 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -48,6 +48,10 @@ extern bool IsInterrupted(); +#if defined(CONF_PLATFORM_ANDROID) +extern std::vector FetchAndroidServerCommandQueue(); +#endif + void CServerBan::InitServerBan(IConsole *pConsole, IStorage *pStorage, CServer *pServer) { CNetBan::Init(pConsole, pStorage); @@ -2937,6 +2941,14 @@ int CServer::Run() m_Fifo.Update(); +#if defined(CONF_PLATFORM_ANDROID) + std::vector vAndroidCommandQueue = FetchAndroidServerCommandQueue(); + for(const std::string &Command : vAndroidCommandQueue) + { + Console()->ExecuteLineFlag(Command.c_str(), CFGFLAG_SERVER, -1); + } +#endif + // master server stuff m_pRegister->Update(); diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index c498bc8c11f..cd26b166b6a 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -74,8 +74,6 @@ CMenus::CMenus() m_DemoPlayerState = DEMOPLAYER_NONE; m_Dummy = false; - m_ServerProcess.m_Process = INVALID_PROCESS; - for(SUIAnimator &animator : m_aAnimatorsSettingsTab) { animator.m_YOffset = -2.5f; diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 8e6d255214a..2af526b37c5 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -33,7 +33,9 @@ struct CServerProcess { - PROCESS m_Process; +#if !defined(CONF_PLATFORM_ANDROID) + PROCESS m_Process = INVALID_PROCESS; +#endif }; // component to fetch keypresses, override all other input @@ -669,7 +671,9 @@ class CMenus : public CComponent bool IsActive() const { return m_MenuActive; } void SetActive(bool Active); + void RunServer(); void KillServer(); + bool IsServerRunning() const; virtual void OnInit() override; void OnConsoleInit() override; diff --git a/src/game/client/components/menus_start.cpp b/src/game/client/components/menus_start.cpp index 0224b7c2672..aa3b11d4fc6 100644 --- a/src/game/client/components/menus_start.cpp +++ b/src/game/client/components/menus_start.cpp @@ -17,6 +17,10 @@ #include "menus.h" +#if defined(CONF_PLATFORM_ANDROID) +#include +#endif + using namespace FontIcons; void CMenus::RenderStartMenu(CUIRect MainView) @@ -124,37 +128,26 @@ void CMenus::RenderStartMenu(CUIRect MainView) if(DoButton_Menu(&s_SettingsButton, Localize("Settings"), 0, &Button, g_Config.m_ClShowStartMenuImages ? "settings" : 0, IGraphics::CORNER_ALL, Rounding, 0.5f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f)) || CheckHotKey(KEY_S)) NewPage = PAGE_SETTINGS; -#if !defined(CONF_PLATFORM_ANDROID) Menu.HSplitBottom(5.0f, &Menu, 0); // little space Menu.HSplitBottom(40.0f, &Menu, &Button); static CButtonContainer s_LocalServerButton; +#if !defined(CONF_PLATFORM_ANDROID) if(!is_process_alive(m_ServerProcess.m_Process)) KillServer(); +#endif - if(DoButton_Menu(&s_LocalServerButton, m_ServerProcess.m_Process ? Localize("Stop server") : Localize("Run server"), 0, &Button, g_Config.m_ClShowStartMenuImages ? "local_server" : 0, IGraphics::CORNER_ALL, Rounding, 0.5f, m_ServerProcess.m_Process ? ColorRGBA(0.0f, 1.0f, 0.0f, 0.25f) : ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f)) || (CheckHotKey(KEY_R) && Input()->KeyPress(KEY_R))) + if(DoButton_Menu(&s_LocalServerButton, IsServerRunning() ? Localize("Stop server") : Localize("Run server"), 0, &Button, g_Config.m_ClShowStartMenuImages ? "local_server" : 0, IGraphics::CORNER_ALL, Rounding, 0.5f, IsServerRunning() ? ColorRGBA(0.0f, 1.0f, 0.0f, 0.25f) : ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f)) || (CheckHotKey(KEY_R) && Input()->KeyPress(KEY_R))) { - if(m_ServerProcess.m_Process) + if(IsServerRunning()) { KillServer(); } else { - char aBuf[IO_MAX_PATH_LENGTH]; - Storage()->GetBinaryPath(PLAT_SERVER_EXEC, aBuf, sizeof(aBuf)); - // No / in binary path means to search in $PATH, so it is expected that the file can't be opened. Just try executing anyway. - if(str_find(aBuf, "/") == 0 || fs_is_file(aBuf)) - { - m_ServerProcess.m_Process = shell_execute(aBuf, EShellExecuteWindowState::BACKGROUND); - m_ForceRefreshLanPage = true; - } - else - { - Client()->AddWarning(SWarning(Localize("Server executable not found, can't run server"))); - } + RunServer(); } } -#endif Menu.HSplitBottom(5.0f, &Menu, 0); // little space Menu.HSplitBottom(40.0f, &Menu, &Button); @@ -283,16 +276,52 @@ void CMenus::RenderStartMenu(CUIRect MainView) } } +void CMenus::RunServer() +{ +#if defined(CONF_PLATFORM_ANDROID) + if(StartAndroidServer()) + { + m_ForceRefreshLanPage = true; + } + else + { + Client()->AddWarning(SWarning(Localize("Server could not be started. Make sure to grant the notification permission in the app settings so the server can run in the background."))); + } +#else + char aBuf[IO_MAX_PATH_LENGTH]; + Storage()->GetBinaryPath(PLAT_SERVER_EXEC, aBuf, sizeof(aBuf)); + // No / in binary path means to search in $PATH, so it is expected that the file can't be opened. Just try executing anyway. + if(str_find(aBuf, "/") == 0 || fs_is_file(aBuf)) + { + m_ServerProcess.m_Process = shell_execute(aBuf, EShellExecuteWindowState::BACKGROUND); + m_ForceRefreshLanPage = true; + } + else + { + Client()->AddWarning(SWarning(Localize("Server executable not found, can't run server"))); + } +#endif +} + void CMenus::KillServer() { -#if !defined(CONF_PLATFORM_ANDROID) - if(m_ServerProcess.m_Process) +#if defined(CONF_PLATFORM_ANDROID) + ExecuteAndroidServerCommand("shutdown"); + m_ForceRefreshLanPage = true; +#else + if(m_ServerProcess.m_Process && kill_process(m_ServerProcess.m_Process)) { - if(kill_process(m_ServerProcess.m_Process)) - { - m_ServerProcess.m_Process = INVALID_PROCESS; - m_ForceRefreshLanPage = true; - } + m_ServerProcess.m_Process = INVALID_PROCESS; + m_ForceRefreshLanPage = true; } #endif } + +bool CMenus::IsServerRunning() const +{ +#if defined(CONF_PLATFORM_ANDROID) + return IsAndroidServerRunning(); +#else + return m_ServerProcess.m_Process != INVALID_PROCESS; +#endif +} From abc9ae72026070cf2d5297c660b220a66b19ad3f Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Mon, 16 Dec 2024 20:39:30 +0800 Subject: [PATCH 23/40] Deobfuscate packet flag comment a bit --- src/engine/shared/network.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h index 7cea700c100..5622d2100fa 100644 --- a/src/engine/shared/network.h +++ b/src/engine/shared/network.h @@ -19,9 +19,19 @@ class CPacker; packet header: 3 bytes unsigned char flags_ack; // 6bit flags, 2bit ack 0.6: ORNCaaAA - 0.6.5: ORNCTUAA + 0.6.5: ORNCT-AA 0.7: --NORCAA + O = flag compression + R = flag resend + N = flag connless + C = flag control + T = flag token (0.6.5 only not supported by ddnet) + - = unused, should be zero + a = should be zero otherwise it messes up the ack number + A = bit of ack number + + unsigned char ack; // 8 bit ack unsigned char num_chunks; // 8 bit chunks From 5cbb41c3aa88dac96ece06efa867773fd805810f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 16 Dec 2024 21:18:28 +0100 Subject: [PATCH 24/40] Fix console behavior when pasting multiple lines of text Ensure all pasted lines are added to the console history instead of only the last one. Ensure the prompt `> [command]` is printed for all pasted lines instead of only for the last one. Fix commands being executed when pasting multiple lines while the console is in searching-mode. Now, newline characters will be replaced with spaces while the input is used for searching. --- src/game/client/components/console.cpp | 59 +++++++++++++++++--------- src/game/client/components/console.h | 3 +- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index 1d9f4ab45bc..3ce0d7a21b0 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -295,12 +295,31 @@ void CGameConsole::CInstance::Reset() void CGameConsole::CInstance::ExecuteLine(const char *pLine) { + if(m_Type == CONSOLETYPE_LOCAL || m_pGameConsole->Client()->RconAuthed()) + { + const char *pPrevEntry = m_History.Last(); + if(pPrevEntry == nullptr || str_comp(pPrevEntry, pLine) != 0) + { + const size_t Size = str_length(pLine) + 1; + char *pEntry = m_History.Allocate(Size); + str_copy(pEntry, pLine, Size); + } + // print out the user's commands before they get run + char aBuf[IConsole::CMDLINE_LENGTH + 3]; + str_format(aBuf, sizeof(aBuf), "> %s", pLine); + m_pGameConsole->PrintLine(m_Type, aBuf); + } + if(m_Type == CGameConsole::CONSOLETYPE_LOCAL) + { m_pGameConsole->m_pConsole->ExecuteLine(pLine); + } else { if(m_pGameConsole->Client()->RconAuthed()) + { m_pGameConsole->Client()->Rcon(pLine); + } else { if(!m_UserGot && m_UsernameReq) @@ -404,22 +423,9 @@ bool CGameConsole::CInstance::OnInput(const IInput::CEvent &Event) { if(!m_Input.IsEmpty() || (m_UsernameReq && !m_pGameConsole->Client()->RconAuthed() && !m_UserGot)) { - if(m_Type == CONSOLETYPE_LOCAL || m_pGameConsole->Client()->RconAuthed()) - { - const char *pPrevEntry = m_History.Last(); - if(pPrevEntry == nullptr || str_comp(pPrevEntry, m_Input.GetString()) != 0) - { - char *pEntry = m_History.Allocate(m_Input.GetLength() + 1); - str_copy(pEntry, m_Input.GetString(), m_Input.GetLength() + 1); - } - // print out the user's commands before they get run - char aBuf[IConsole::CMDLINE_LENGTH + 3]; - str_format(aBuf, sizeof(aBuf), "> %s", m_Input.GetString()); - m_pGameConsole->PrintLine(m_Type, aBuf); - } ExecuteLine(m_Input.GetString()); m_Input.Clear(); - m_pHistoryEntry = 0x0; + m_pHistoryEntry = nullptr; } } else @@ -577,16 +583,12 @@ bool CGameConsole::CInstance::OnInput(const IInput::CEvent &Event) } else if(Event.m_Key == KEY_ESCAPE && m_Searching) { - m_Searching = false; - m_Input.Clear(); + SetSearching(false); Handled = true; } else if(Event.m_Key == KEY_F && m_pGameConsole->Input()->ModifierIsPressed()) { - m_Searching = true; - m_Input.Set(m_aCurrentSearchString); - m_Input.SelectAll(); - UpdateSearch(); + SetSearching(true); Handled = true; } } @@ -725,6 +727,23 @@ void CGameConsole::CInstance::UpdateEntryTextAttributes(CBacklogEntry *pEntry) c pEntry->m_LineCount = Cursor.m_LineCount; } +void CGameConsole::CInstance::SetSearching(bool Searching) +{ + m_Searching = Searching; + if(Searching) + { + m_Input.SetClipboardLineCallback(nullptr); // restore default behavior (replace newlines with spaces) + m_Input.Set(m_aCurrentSearchString); + m_Input.SelectAll(); + UpdateSearch(); + } + else + { + m_Input.SetClipboardLineCallback([this](const char *pLine) { ExecuteLine(pLine); }); + m_Input.Clear(); + } +} + void CGameConsole::CInstance::ClearSearch() { m_vSearchMatches.clear(); diff --git a/src/game/client/components/console.h b/src/game/client/components/console.h index f411d0323d4..3ad5355d64f 100644 --- a/src/game/client/components/console.h +++ b/src/game/client/components/console.h @@ -112,7 +112,6 @@ class CGameConsole : public CComponent void PrintLine(const char *pLine, int Len, ColorRGBA PrintColor) REQUIRES(!m_BacklogPendingLock); int GetLinesToScroll(int Direction, int LinesToScroll); void ScrollToCenter(int StartLine, int EndLine); - void ClearSearch(); void Dump() REQUIRES(!m_BacklogPendingLock); const char *GetString() const { return m_Input.GetString(); } @@ -135,6 +134,8 @@ class CGameConsole : public CComponent void UpdateEntryTextAttributes(CBacklogEntry *pEntry) const; private: + void SetSearching(bool Searching); + void ClearSearch(); void UpdateSearch(); friend class CGameConsole; From c6dd144223bdb6d3caad330153234488a6733d3d Mon Sep 17 00:00:00 2001 From: Pioooooo Date: Tue, 17 Dec 2024 07:05:12 +0800 Subject: [PATCH 25/40] Fix jump effect opacity --- src/game/client/gameclient.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index cb779f6ea2a..90b79c9e805 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -2153,8 +2153,7 @@ void CGameClient::OnNewSnapshot() vec2(m_Snap.m_aCharacters[i].m_Cur.m_X, m_Snap.m_aCharacters[i].m_Cur.m_Y), Client()->IntraGameTick(g_Config.m_ClDummy)); float Alpha = 1.0f; - bool SameTeam = m_Teams.SameTeam(m_Snap.m_LocalClientId, i); - if(!SameTeam || m_aClients[i].m_Solo || m_aClients[m_Snap.m_LocalClientId].m_Solo) + if(IsOtherTeam(i)) Alpha = g_Config.m_ClShowOthersAlpha / 100.0f; m_Effects.AirJump(Pos, Alpha); } From aec89d58337083e1d11b67620c1a3cda5f5ef8b5 Mon Sep 17 00:00:00 2001 From: ASKLL Date: Tue, 17 Dec 2024 15:27:07 +0800 Subject: [PATCH 26/40] use remote tee angle when spectating --- src/game/client/components/players.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 525f13612ed..e0240f23070 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -186,7 +186,7 @@ void CPlayers::RenderHookCollLine( IntraTick = m_pClient->m_aClients[ClientId].m_IsPredicted ? Client()->PredIntraGameTick(g_Config.m_ClDummy) : Client()->IntraGameTick(g_Config.m_ClDummy); float Angle; - if(Local && (!m_pClient->m_Snap.m_SpecInfo.m_Active || m_pClient->m_Snap.m_SpecInfo.m_SpectatorId != SPEC_FREEVIEW) && Client()->State() != IClient::STATE_DEMOPLAYBACK) + if(Local && !m_pClient->m_Snap.m_SpecInfo.m_Active && Client()->State() != IClient::STATE_DEMOPLAYBACK) { // just use the direct input if it's the local player we are rendering Angle = angle(m_pClient->m_Controls.m_aMousePos[g_Config.m_ClDummy] * m_pClient->m_Camera.m_Zoom); @@ -230,7 +230,7 @@ void CPlayers::RenderHookCollLine( { vec2 ExDirection = Direction; - if(Local && (!m_pClient->m_Snap.m_SpecInfo.m_Active || m_pClient->m_Snap.m_SpecInfo.m_SpectatorId != SPEC_FREEVIEW) && Client()->State() != IClient::STATE_DEMOPLAYBACK) + if(Local && !m_pClient->m_Snap.m_SpecInfo.m_Active && Client()->State() != IClient::STATE_DEMOPLAYBACK) { ExDirection = normalize( vec2((int)((int)m_pClient->m_Controls.m_aMousePos[g_Config.m_ClDummy].x * m_pClient->m_Camera.m_Zoom), From 41e37fc0ad8b12cd9a83314a8d87f589dd00edba Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Tue, 17 Dec 2024 19:15:09 +0300 Subject: [PATCH 27/40] Add template EscapeUrl() which automatically obtains the given array size --- src/engine/client/client.cpp | 2 +- src/engine/shared/http.h | 7 +++++++ src/game/client/components/skins.cpp | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 97ef74d2b57..538ebb33d5c 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -1582,7 +1582,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy) { char aUrl[256]; char aEscaped[256]; - EscapeUrl(aEscaped, sizeof(aEscaped), m_aMapdownloadFilename + 15); // cut off downloadedmaps/ + EscapeUrl(aEscaped, m_aMapdownloadFilename + 15); // cut off downloadedmaps/ bool UseConfigUrl = str_comp(g_Config.m_ClMapDownloadUrl, "https://maps.ddnet.org") != 0 || m_aMapDownloadUrl[0] == '\0'; str_format(aUrl, sizeof(aUrl), "%s/%s", UseConfigUrl ? g_Config.m_ClMapDownloadUrl : m_aMapDownloadUrl, aEscaped); diff --git a/src/engine/shared/http.h b/src/engine/shared/http.h index 27ecbb05d4a..f7a5d9836f4 100644 --- a/src/engine/shared/http.h +++ b/src/engine/shared/http.h @@ -259,6 +259,13 @@ inline std::unique_ptr HttpPostJson(const char *pUrl, const char * } void EscapeUrl(char *pBuf, int Size, const char *pStr); + +template +void EscapeUrl(char (&aBuf)[N], const char *pStr) +{ + EscapeUrl(aBuf, N, pStr); +} + bool HttpHasIpresolveBug(); // In an ideal world this would be a kernel interface diff --git a/src/game/client/components/skins.cpp b/src/game/client/components/skins.cpp index 3daa6e45304..8c1514758fb 100644 --- a/src/game/client/components/skins.cpp +++ b/src/game/client/components/skins.cpp @@ -471,7 +471,7 @@ void CSkins::CSkinDownloadJob::Run() const char *pBaseUrl = g_Config.m_ClDownloadCommunitySkins != 0 ? g_Config.m_ClSkinCommunityDownloadUrl : g_Config.m_ClSkinDownloadUrl; char aEscapedName[256]; - EscapeUrl(aEscapedName, sizeof(aEscapedName), m_aName); + EscapeUrl(aEscapedName, m_aName); char aUrl[IO_MAX_PATH_LENGTH]; str_format(aUrl, sizeof(aUrl), "%s%s.png", pBaseUrl, aEscapedName); From d9573fb118507e241fa84102b3bb9e34c77d8c0f Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Tue, 17 Dec 2024 00:05:32 +0300 Subject: [PATCH 28/40] Add 'sv_maps_base_url' to support map download https urls --- src/engine/server/server.cpp | 20 +++++++++++++++++++- src/engine/server/server.h | 1 + src/engine/shared/config_variables.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 242639f85b3..a0e9aa927a7 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -251,6 +251,7 @@ CServer::CServer() m_ReloadedWhenEmpty = false; m_aCurrentMap[0] = '\0'; m_pCurrentMapName = m_aCurrentMap; + m_aMapDownloadUrl[0] = '\0'; m_RconClientId = IServer::RCON_CID_SERV; m_RconAuthLevel = AUTHED_ADMIN; @@ -1214,7 +1215,14 @@ void CServer::SendMap(int ClientId) Msg.AddRaw(&m_aCurrentMapSha256[MapType].data, sizeof(m_aCurrentMapSha256[MapType].data)); Msg.AddInt(m_aCurrentMapCrc[MapType]); Msg.AddInt(m_aCurrentMapSize[MapType]); - Msg.AddString("", 0); // HTTPS map download URL + if(m_aMapDownloadUrl[0]) + { + Msg.AddString(m_aMapDownloadUrl, 0); + } + else + { + Msg.AddString("", 0); + } SendMsg(&Msg, MSGFLAG_VITAL, ClientId); } { @@ -2591,6 +2599,16 @@ int CServer::LoadMap(const char *pMapName) m_apCurrentMapData[MAP_TYPE_SIX] = (unsigned char *)pData; } + if(Config()->m_SvMapsBaseUrl[0]) + { + str_format(aBuf, sizeof(aBuf), "%s%s_%s.map", Config()->m_SvMapsBaseUrl, pMapName, aSha256); + EscapeUrl(m_aMapDownloadUrl, aBuf); + } + else + { + m_aMapDownloadUrl[0] = '\0'; + } + // load sixup version of the map if(Config()->m_SvSixup) { diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 36ba63a42ac..ea2d541f3e8 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -247,6 +247,7 @@ class CServer : public IServer unsigned m_aCurrentMapCrc[NUM_MAP_TYPES]; unsigned char *m_apCurrentMapData[NUM_MAP_TYPES]; unsigned int m_aCurrentMapSize[NUM_MAP_TYPES]; + char m_aMapDownloadUrl[256]; CDemoRecorder m_aDemoRecorder[NUM_RECORDERS]; CAuthManager m_AuthManager; diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 1390c105e94..28b9a68ce5f 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -439,6 +439,7 @@ MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "U MACRO_CONFIG_STR(SvRegister, sv_register, 16, "1", CFGFLAG_SERVER, "Register server with master server for public listing, can also accept a comma-separated list of protocols to register on, like 'ipv4,ipv6'") MACRO_CONFIG_STR(SvRegisterExtra, sv_register_extra, 256, "", CFGFLAG_SERVER, "Extra headers to send to the register endpoint, comma separated 'Header: Value' pairs") MACRO_CONFIG_STR(SvRegisterUrl, sv_register_url, 128, "https://master1.ddnet.org/ddnet/15/register", CFGFLAG_SERVER, "Masterserver URL to register to") +MACRO_CONFIG_STR(SvMapsBaseUrl, sv_maps_base_url, 128, "", CFGFLAG_SERVER, "Base path used to provide HTTPS map download URL to the clients") MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 128, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Remote console password (full access)") MACRO_CONFIG_STR(SvRconModPassword, sv_rcon_mod_password, 128, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Remote console password for moderators (limited access)") MACRO_CONFIG_STR(SvRconHelperPassword, sv_rcon_helper_password, 128, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Remote console password for helpers (limited access)") From d7ae3a3076af726c2cc2be1185de367cc10df2d9 Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Wed, 18 Dec 2024 12:18:14 +0800 Subject: [PATCH 29/40] Replace magic numbers with compile time string length The ``sizeof()`` macro is evaluated at compile time. The string passed to it does not end up in the final binary and there is also no counting happening at runtime. The ``sizeof()`` operator counts the null terminator too. So ``sizeof("foo")`` is 4 and not 3. But this comes in handy because we want to skip over the length of the command and the slash character in front of it. --- src/game/server/gamecontext.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 7a03e2fb7d4..86ce39add39 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -2179,25 +2179,25 @@ void CGameContext::OnSayNetMessage(const CNetMsg_Cl_Say *pMsg, int ClientId, con if(str_startswith_nocase(pMsg->m_pMessage + 1, "w ")) { char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + 3); + str_copy(aWhisperMsg, pMsg->m_pMessage + sizeof("w ")); Whisper(pPlayer->GetCid(), aWhisperMsg); } else if(str_startswith_nocase(pMsg->m_pMessage + 1, "whisper ")) { char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + 9); + str_copy(aWhisperMsg, pMsg->m_pMessage + sizeof("whisper ")); Whisper(pPlayer->GetCid(), aWhisperMsg); } else if(str_startswith_nocase(pMsg->m_pMessage + 1, "c ")) { char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + 3); + str_copy(aWhisperMsg, pMsg->m_pMessage + sizeof("c ")); Converse(pPlayer->GetCid(), aWhisperMsg); } else if(str_startswith_nocase(pMsg->m_pMessage + 1, "converse ")) { char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + 10); + str_copy(aWhisperMsg, pMsg->m_pMessage + sizeof("converse ")); Converse(pPlayer->GetCid(), aWhisperMsg); } else From b2ff581a0f6af9fe5817a751e0e84cb8aebbe310 Mon Sep 17 00:00:00 2001 From: Pioooooo Date: Thu, 19 Dec 2024 04:55:44 +0800 Subject: [PATCH 30/40] Ignore case when map voting with full match --- src/game/server/scoreworker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/server/scoreworker.cpp b/src/game/server/scoreworker.cpp index a77dadd936d..7f825a71edd 100644 --- a/src/game/server/scoreworker.cpp +++ b/src/game/server/scoreworker.cpp @@ -322,7 +322,7 @@ bool CScoreWorker::MapVote(IDbConnection *pSqlServer, const ISqlData *pGameData, "FROM %s_maps " "WHERE Map LIKE %s " "ORDER BY " - " CASE WHEN Map = ? THEN 0 ELSE 1 END, " + " CASE WHEN LOWER(Map) = LOWER(?) THEN 0 ELSE 1 END, " " CASE WHEN Map LIKE ? THEN 0 ELSE 1 END, " " LENGTH(Map), Map " "LIMIT 1", @@ -395,7 +395,7 @@ bool CScoreWorker::MapInfo(IDbConnection *pSqlServer, const ISqlData *pGameData, " SELECT * FROM %s_maps " " WHERE Map LIKE %s " " ORDER BY " - " CASE WHEN Map = ? THEN 0 ELSE 1 END, " + " CASE WHEN LOWER(Map) = LOWER(?) THEN 0 ELSE 1 END, " " CASE WHEN Map LIKE ? THEN 0 ELSE 1 END, " " LENGTH(Map), " " Map " From db72be9b500c8c8484c62a47e7f47a83e60a784e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 18 Dec 2024 22:10:28 +0100 Subject: [PATCH 31/40] Refactor main menu hotkey handling - Also prevent main menu hotkey activating when Alt-modifier is pressed. - Prevent repeated hotkey activating by using `IInput::KeyPress` instead of `IInput::KeyIsPressed` in `CMenus::CheckHotKey`. - Remove unnecessary code for preventing editor hotkey from being activated quickly, which is made obsolete by using `IInput::KeyPress`. - Remove unnecessary check for fullscreen popup which is currently always true and also an unnecessary restriction for hotkeys. --- src/game/client/components/menus.cpp | 6 +++--- src/game/client/components/menus.h | 2 -- src/game/client/components/menus_start.cpp | 8 +------- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index cd26b166b6a..57225590453 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -2387,9 +2387,9 @@ void CMenus::RenderBackground() bool CMenus::CheckHotKey(int Key) const { - return m_Popup == POPUP_NONE && - !Input()->ShiftIsPressed() && !Input()->ModifierIsPressed() && // no modifier - Input()->KeyIsPressed(Key) && m_pClient->m_GameConsole.IsClosed(); + return !Input()->ShiftIsPressed() && !Input()->ModifierIsPressed() && !Input()->AltIsPressed() && // no modifier + Input()->KeyPress(Key) && + m_pClient->m_GameConsole.IsClosed(); } int CMenus::DoButton_CheckBox_Tristate(const void *pId, const char *pText, TRISTATE Checked, const CUIRect *pRect) diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 2af526b37c5..2437f9e1f12 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -478,8 +478,6 @@ class CMenus : public CComponent // found in menus_start.cpp void RenderStartMenu(CUIRect MainView); - bool m_EditorHotkeyWasPressed = true; - float m_EditorHotKeyChecktime = 0.0f; // found in menus_ingame.cpp STextContainerIndex m_MotdTextContainerIndex; diff --git a/src/game/client/components/menus_start.cpp b/src/game/client/components/menus_start.cpp index aa3b11d4fc6..3ba49b2c8f2 100644 --- a/src/game/client/components/menus_start.cpp +++ b/src/game/client/components/menus_start.cpp @@ -152,16 +152,10 @@ void CMenus::RenderStartMenu(CUIRect MainView) Menu.HSplitBottom(5.0f, &Menu, 0); // little space Menu.HSplitBottom(40.0f, &Menu, &Button); static CButtonContainer s_MapEditorButton; - if(DoButton_Menu(&s_MapEditorButton, Localize("Editor"), 0, &Button, g_Config.m_ClShowStartMenuImages ? "editor" : 0, IGraphics::CORNER_ALL, Rounding, 0.5f, m_pClient->Editor()->HasUnsavedData() ? ColorRGBA(0.0f, 1.0f, 0.0f, 0.25f) : ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f)) || (!m_EditorHotkeyWasPressed && Client()->LocalTime() - m_EditorHotKeyChecktime < 0.1f && CheckHotKey(KEY_E))) + if(DoButton_Menu(&s_MapEditorButton, Localize("Editor"), 0, &Button, g_Config.m_ClShowStartMenuImages ? "editor" : 0, IGraphics::CORNER_ALL, Rounding, 0.5f, m_pClient->Editor()->HasUnsavedData() ? ColorRGBA(0.0f, 1.0f, 0.0f, 0.25f) : ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f)) || CheckHotKey(KEY_E)) { g_Config.m_ClEditor = 1; Input()->MouseModeRelative(); - m_EditorHotkeyWasPressed = true; - } - if(!Input()->KeyIsPressed(KEY_E)) - { - m_EditorHotkeyWasPressed = false; - m_EditorHotKeyChecktime = Client()->LocalTime(); } Menu.HSplitBottom(5.0f, &Menu, 0); // little space From ea9a0a3a9c7947644fd0bba950e08d174c321797 Mon Sep 17 00:00:00 2001 From: TsFreddie Date: Sun, 1 Dec 2024 00:45:04 +0800 Subject: [PATCH 32/40] spectator cursor and auto spec camera --- datasrc/network.py | 7 + src/base/math.h | 15 ++ src/engine/shared/config_variables.h | 2 + src/engine/textrender.h | 2 + src/game/client/components/camera.cpp | 157 +++++++++++++++--- src/game/client/components/camera.h | 24 ++- src/game/client/components/hud.cpp | 85 +++++++++- src/game/client/components/menus_demo.cpp | 13 ++ src/game/client/components/menus_settings.cpp | 2 +- src/game/client/components/spectator.cpp | 9 + src/game/client/gameclient.cpp | 155 ++++++++++++++++- src/game/client/gameclient.h | 38 ++++- src/game/server/player.cpp | 21 +++ src/game/server/player.h | 2 + 14 files changed, 494 insertions(+), 38 deletions(-) diff --git a/datasrc/network.py b/datasrc/network.py index d03edce9795..6db7b50d798 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -316,6 +316,13 @@ NetIntAny("m_SwitchNumber"), ]), + NetObjectEx("DDNetSpectatorInfo", "spectator-info@netobj.ddnet.org", [ + NetBool("m_HasCameraInfo"), + NetIntRange("m_Zoom", 0, 'max_int'), + NetIntRange("m_Deadzone", 0, 'max_int'), + NetIntRange("m_FollowFactor", 0, 'max_int'), + ]), + ## Events NetEvent("Common", [ diff --git a/src/base/math.h b/src/base/math.h index b4b553bbc4e..cd8e9214f48 100644 --- a/src/base/math.h +++ b/src/base/math.h @@ -41,6 +41,21 @@ inline T bezier(const T p0, const T p1, const T p2, const T p3, TB amount) return mix(c20, c21, amount); // c30 } +template +inline T mix_polynomial(const TB time[], const T data[], int samples, TB amount, T init) +{ + T result = init; + for(int i = 0; i < samples; i++) + { + T term = data[i]; + for(int j = 0; j < samples; j++) + if(j != i) + term = term * (amount - time[j]) / TB(time[i] - time[j]); + result += term; + } + return result; +} + inline float random_float() { return rand() / (float)(RAND_MAX); diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index d676b4e8a82..f49e495d2f2 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -80,6 +80,8 @@ MACRO_CONFIG_INT(ClEyeWheel, cl_eye_wheel, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAV MACRO_CONFIG_INT(ClEyeDuration, cl_eye_duration, 999999, 1, 999999, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How long the eyes emotes last") MACRO_CONFIG_INT(ClFreezeStars, cl_freeze_stars, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show old star particles for frozen tees") +MACRO_CONFIG_INT(ClSpecCursor, cl_spec_cursor, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Enable the cursor of spectating player if available") + MACRO_CONFIG_INT(ClAirjumpindicator, cl_airjumpindicator, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show the air jump indicator") MACRO_CONFIG_INT(ClThreadsoundloading, cl_threadsoundloading, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Load sound files threaded") diff --git a/src/engine/textrender.h b/src/engine/textrender.h index 2c7e6561ca5..0b728f307ad 100644 --- a/src/engine/textrender.h +++ b/src/engine/textrender.h @@ -149,6 +149,8 @@ MAYBE_UNUSED static const char *FONT_ICON_REDO = "\xEF\x8B\xB9"; MAYBE_UNUSED static const char *FONT_ICON_ARROWS_ROTATE = "\xEF\x80\xA1"; MAYBE_UNUSED static const char *FONT_ICON_QUESTION = "?"; + +MAYBE_UNUSED static const char *FONT_ICON_CAMERA = "\xEF\x80\xB0"; } // end namespace FontIcons enum ETextCursorSelectionMode diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp index 2d64955bec5..66213fb95a1 100644 --- a/src/game/client/components/camera.cpp +++ b/src/game/client/components/camera.cpp @@ -36,10 +36,14 @@ CCamera::CCamera() m_CameraSmoothing = false; - m_LastMousePos = vec2(0, 0); + m_LastTargetPos = vec2(0, 0); m_DyncamTargetCameraOffset = vec2(0, 0); mem_zero(m_aDyncamCurrentCameraOffset, sizeof(m_aDyncamCurrentCameraOffset)); m_DyncamSmoothingSpeedBias = 0.5f; + + m_AutoSpecCamera = true; + m_AutoSpecCameraZooming = false; + m_UsingAutoSpecCamera = false; } float CCamera::CameraSmoothingProgress(float CurrentTime) const @@ -56,7 +60,15 @@ float CCamera::ZoomProgress(float CurrentTime) const void CCamera::ScaleZoom(float Factor) { float CurrentTarget = m_Zooming ? m_ZoomSmoothingTarget : m_Zoom; - ChangeZoom(CurrentTarget * Factor, m_pClient->m_Snap.m_SpecInfo.m_Active && GameClient()->m_MultiViewActivated ? g_Config.m_ClMultiViewZoomSmoothness : g_Config.m_ClSmoothZoomTime); + bool IsUserZoom = !m_IsSpectatingPlayer; + + ChangeZoom(CurrentTarget * Factor, m_pClient->m_Snap.m_SpecInfo.m_Active && GameClient()->m_MultiViewActivated ? g_Config.m_ClMultiViewZoomSmoothness : g_Config.m_ClSmoothZoomTime, IsUserZoom); + + if(m_IsSpectatingPlayer) + { + m_AutoSpecCamera = false; + m_SpecZoomTarget = m_ZoomSmoothingTarget; + } } float CCamera::MaxZoomLevel() @@ -69,7 +81,7 @@ float CCamera::MinZoomLevel() return 0.01f; } -void CCamera::ChangeZoom(float Target, int Smoothness) +void CCamera::ChangeZoom(float Target, int Smoothness, bool IsUser) { if(Target > MaxZoomLevel() || Target < MinZoomLevel()) { @@ -91,11 +103,47 @@ void CCamera::ChangeZoom(float Target, int Smoothness) m_ZoomSmoothingStart = Now; m_ZoomSmoothingEnd = Now + (float)Smoothness / 1000; + if(IsUser) + m_UserZoomTarget = Target; + m_Zooming = true; } +void CCamera::ResetAutoSpecCamera() +{ + m_AutoSpecCamera = true; + m_SpecZoomTarget = CCamera::ZoomStepsToValue(g_Config.m_ClDefaultZoom - 10); +} + void CCamera::UpdateCamera() { + // use hardcoded smooth camera for spectating unless player explictly turn it off + bool IsSpectatingPlayer = !GameClient()->m_MultiViewActivated; + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + IsSpectatingPlayer = IsSpectatingPlayer && m_pClient->m_Snap.m_SpecInfo.m_SpectatorId >= 0; + else + IsSpectatingPlayer = IsSpectatingPlayer && m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_SpectatorId >= 0; + + bool UsingAutoSpecCamera = m_AutoSpecCamera && CanUseAutoSpecCamera(); + float CurrentZoom = m_Zooming ? m_ZoomSmoothingTarget : m_Zoom; + + if(IsSpectatingPlayer && UsingAutoSpecCamera && CurrentZoom != m_pClient->m_Snap.m_SpecInfo.m_Zoom) + { + ChangeZoom(m_pClient->m_Snap.m_SpecInfo.m_Zoom, 250, false); + // it is auto spec camera zooming if only the zoom is changed during activation, not at the start of the activation + m_AutoSpecCameraZooming = IsSpectatingPlayer && m_UsingAutoSpecCamera; + } + else if((IsSpectatingPlayer && !UsingAutoSpecCamera) && CurrentZoom != m_SpecZoomTarget) + { + ChangeZoom(m_SpecZoomTarget, g_Config.m_ClSmoothZoomTime, false); + m_AutoSpecCameraZooming = false; + } + else if(!IsSpectatingPlayer && CurrentZoom != m_UserZoomTarget) + { + ChangeZoom(m_UserZoomTarget, GameClient()->m_MultiViewActivated ? g_Config.m_ClMultiViewZoomSmoothness : g_Config.m_ClSmoothZoomTime, false); + m_AutoSpecCameraZooming = false; + } + if(m_Zooming) { float Time = Client()->LocalTime(); @@ -130,19 +178,29 @@ void CCamera::UpdateCamera() } if(m_pClient->m_Snap.m_SpecInfo.m_Active && !m_pClient->m_Snap.m_SpecInfo.m_UsePosition) + { + m_aDyncamCurrentCameraOffset[g_Config.m_ClDummy] = vec2(0, 0); + m_IsSpectatingPlayer = IsSpectatingPlayer; + m_UsingAutoSpecCamera = UsingAutoSpecCamera; return; + } + + vec2 TargetPos = IsSpectatingPlayer ? m_pClient->m_CursorInfo.Target() : m_pClient->m_Controls.m_aMousePos[g_Config.m_ClDummy]; + int Smoothness = IsSpectatingPlayer ? 50 : g_Config.m_ClDyncamSmoothness; + int Stabilizing = IsSpectatingPlayer ? 50 : g_Config.m_ClDyncamStabilizing; + bool IsDyncam = IsSpectatingPlayer ? true : g_Config.m_ClDyncam; float DeltaTime = Client()->RenderFrameTime(); - if(g_Config.m_ClDyncamSmoothness > 0) + if(Smoothness > 0) { - float CameraSpeed = (1.0f - (g_Config.m_ClDyncamSmoothness / 100.0f)) * 9.5f + 0.5f; - float CameraStabilizingFactor = 1 + g_Config.m_ClDyncamStabilizing / 100.0f; + float CameraSpeed = (1.0f - (Smoothness / 100.0f)) * 9.5f + 0.5f; + float CameraStabilizingFactor = 1 + Stabilizing / 100.0f; m_DyncamSmoothingSpeedBias += CameraSpeed * DeltaTime; - if(g_Config.m_ClDyncam) + if(IsDyncam) { - m_DyncamSmoothingSpeedBias -= length(m_pClient->m_Controls.m_aMousePos[g_Config.m_ClDummy] - m_LastMousePos) * std::log10(CameraStabilizingFactor) * 0.02f; + m_DyncamSmoothingSpeedBias -= length(TargetPos - m_LastTargetPos) * std::log10(CameraStabilizingFactor) * 0.02f; m_DyncamSmoothingSpeedBias = clamp(m_DyncamSmoothingSpeedBias, 0.5f, CameraSpeed); } else @@ -152,19 +210,47 @@ void CCamera::UpdateCamera() } m_DyncamTargetCameraOffset = vec2(0, 0); - vec2 MousePos = m_pClient->m_Controls.m_aMousePos[g_Config.m_ClDummy]; - float l = length(MousePos); + float l = length(TargetPos); if(l > 0.0001f) // make sure that this isn't 0 { - float OffsetAmount = maximum(l - Deadzone(), 0.0f) * (FollowFactor() / 100.0f); - m_DyncamTargetCameraOffset = normalize_pre_length(MousePos, l) * OffsetAmount; + float CurrentDeadzone = Deadzone(); + float CurrentFollowFactor = FollowFactor(); + + // use provided camera setting from server + if(IsSpectatingPlayer) + { + CurrentDeadzone = m_pClient->m_Snap.m_SpecInfo.m_Deadzone; + CurrentFollowFactor = m_pClient->m_Snap.m_SpecInfo.m_FollowFactor; + + if(!UsingAutoSpecCamera) + { + // turn off dyncam if user zooms when spectating + CurrentDeadzone = 0; + CurrentFollowFactor = 0; + } + } + + float OffsetAmount = maximum(l - CurrentDeadzone, 0.0f) * (CurrentFollowFactor / 100.0f); + m_DyncamTargetCameraOffset = normalize(TargetPos) * OffsetAmount; } - m_LastMousePos = MousePos; - if(g_Config.m_ClDyncamSmoothness > 0) - m_aDyncamCurrentCameraOffset[g_Config.m_ClDummy] += (m_DyncamTargetCameraOffset - m_aDyncamCurrentCameraOffset[g_Config.m_ClDummy]) * minimum(DeltaTime * m_DyncamSmoothingSpeedBias, 1.0f); + m_LastTargetPos = TargetPos; + vec2 CurrentCameraOffset = m_aDyncamCurrentCameraOffset[g_Config.m_ClDummy]; + float SpeedBias = m_CameraSmoothing ? 50.0f : m_DyncamSmoothingSpeedBias; + if(Smoothness > 0) + CurrentCameraOffset += (m_DyncamTargetCameraOffset - CurrentCameraOffset) * minimum(DeltaTime * SpeedBias, 1.0f); else - m_aDyncamCurrentCameraOffset[g_Config.m_ClDummy] = m_DyncamTargetCameraOffset; + CurrentCameraOffset = m_DyncamTargetCameraOffset; + + // directly put the camera in place when switching in and out of freeview or spectate mode + if(m_IsSpectatingPlayer != IsSpectatingPlayer) + { + CurrentCameraOffset = m_DyncamTargetCameraOffset; + } + + m_aDyncamCurrentCameraOffset[g_Config.m_ClDummy] = CurrentCameraOffset; + m_IsSpectatingPlayer = IsSpectatingPlayer; + m_UsingAutoSpecCamera = UsingAutoSpecCamera; } void CCamera::OnRender() @@ -295,8 +381,10 @@ void CCamera::OnReset() { m_CameraSmoothing = false; - m_Zoom = std::pow(CCamera::ZOOM_STEP, g_Config.m_ClDefaultZoom - 10); + m_Zoom = CCamera::ZoomStepsToValue(g_Config.m_ClDefaultZoom - 10); m_Zooming = false; + m_UserZoomTarget = CCamera::ZoomStepsToValue(g_Config.m_ClDefaultZoom - 10); + m_SpecZoomTarget = CCamera::ZoomStepsToValue(g_Config.m_ClDefaultZoom - 10); } void CCamera::ConZoomPlus(IConsole::IResult *pResult, void *pUserData) @@ -307,7 +395,7 @@ void CCamera::ConZoomPlus(IConsole::IResult *pResult, void *pUserData) float ZoomAmount = pResult->NumArguments() ? pResult->GetFloat(0) : 1.0f; - pSelf->ScaleZoom(std::pow(CCamera::ZOOM_STEP, ZoomAmount)); + pSelf->ScaleZoom(CCamera::ZoomStepsToValue(ZoomAmount)); if(pSelf->GameClient()->m_MultiViewActivated) pSelf->GameClient()->m_MultiViewPersonalZoom += ZoomAmount; @@ -321,7 +409,7 @@ void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData) float ZoomAmount = pResult->NumArguments() ? pResult->GetFloat(0) : 1.0f; ZoomAmount *= -1.0f; - pSelf->ScaleZoom(std::pow(CCamera::ZOOM_STEP, ZoomAmount)); + pSelf->ScaleZoom(CCamera::ZoomStepsToValue(ZoomAmount)); if(pSelf->GameClient()->m_MultiViewActivated) pSelf->GameClient()->m_MultiViewPersonalZoom += ZoomAmount; @@ -332,8 +420,20 @@ void CCamera::ConZoom(IConsole::IResult *pResult, void *pUserData) if(!pSelf->ZoomAllowed()) return; - float TargetLevel = pResult->NumArguments() ? pResult->GetFloat(0) : g_Config.m_ClDefaultZoom; - pSelf->ChangeZoom(std::pow(CCamera::ZOOM_STEP, TargetLevel - 10.0f), pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active && pSelf->GameClient()->m_MultiViewActivated ? g_Config.m_ClMultiViewZoomSmoothness : g_Config.m_ClSmoothZoomTime); + bool IsReset = !pResult->NumArguments(); + bool IsSpectating = pSelf->m_IsSpectatingPlayer; + + float TargetLevel = !IsReset ? pResult->GetFloat(0) : g_Config.m_ClDefaultZoom; + if(IsSpectating && IsReset) + pSelf->ResetAutoSpecCamera(); + else + pSelf->ChangeZoom(CCamera::ZoomStepsToValue(TargetLevel - 10.0f), pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active && pSelf->GameClient()->m_MultiViewActivated ? g_Config.m_ClMultiViewZoomSmoothness : g_Config.m_ClSmoothZoomTime, true); + + if(IsSpectating && !IsReset) + { + pSelf->m_AutoSpecCamera = false; + pSelf->m_SpecZoomTarget = pSelf->m_ZoomSmoothingTarget; + } if(pSelf->GameClient()->m_MultiViewActivated && pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active) pSelf->GameClient()->m_MultiViewPersonalZoom = TargetLevel - 10.0f; @@ -473,9 +573,9 @@ void CCamera::GotoTele(int Number, int Offset) SetView(MatchPos); } -void CCamera::SetZoom(float Target, int Smoothness) +void CCamera::SetZoom(float Target, int Smoothness, bool IsUser) { - ChangeZoom(Target, Smoothness); + ChangeZoom(Target, Smoothness, IsUser); } bool CCamera::ZoomAllowed() const @@ -494,3 +594,14 @@ int CCamera::FollowFactor() const { return g_Config.m_ClDyncam ? g_Config.m_ClDyncamFollowFactor : g_Config.m_ClMouseFollowfactor; } + +bool CCamera::CanUseAutoSpecCamera() const +{ + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + { + // only follow mode has the correct camera info + return m_pClient->m_Snap.m_SpecInfo.m_HasCameraInfo && m_pClient->m_DemoSpecId == SPEC_FOLLOW; + } + + return m_pClient->m_Snap.m_SpecInfo.m_HasCameraInfo && m_pClient->m_Snap.m_SpecInfo.m_SpectatorId != m_pClient->m_Snap.m_LocalClientId; +} diff --git a/src/game/client/components/camera.h b/src/game/client/components/camera.h index 32d7c3a10c5..385a293a3e2 100644 --- a/src/game/client/components/camera.h +++ b/src/game/client/components/camera.h @@ -46,24 +46,39 @@ class CCamera : public CComponent float m_ZoomSmoothingEnd; void ScaleZoom(float Factor); - void ChangeZoom(float Target, int Smoothness); + void ChangeZoom(float Target, int Smoothness, bool IsUser); float ZoomProgress(float CurrentTime) const; float MinZoomLevel(); float MaxZoomLevel(); - vec2 m_LastMousePos; + vec2 m_LastTargetPos; float m_DyncamSmoothingSpeedBias; + bool m_IsSpectatingPlayer; + bool m_UsingAutoSpecCamera; public: static constexpr float ZOOM_STEP = 0.866025f; + /** + * Convert zoom steps to zoom value + * + * @param Steps - Zoom steps, 0.0f converts to default zoom (returns 1.0f) + * @return converted zoom value + **/ + static inline float ZoomStepsToValue(float Steps) { return std::pow(CCamera::ZOOM_STEP, Steps); } + vec2 m_Center; bool m_ZoomSet; bool m_Zooming; float m_Zoom; float m_ZoomSmoothingTarget; + bool m_AutoSpecCameraZooming; + bool m_AutoSpecCamera; + float m_UserZoomTarget; + float m_SpecZoomTarget; + vec2 m_DyncamTargetCameraOffset; vec2 m_aDyncamCurrentCameraOffset[NUM_DUMMIES]; @@ -80,7 +95,7 @@ class CCamera : public CComponent void GotoSwitch(int Number, int Offset = -1); void GotoTele(int Number, int Offset = -1); - void SetZoom(float Target, int Smoothness); + void SetZoom(float Target, int Smoothness, bool IsUser); bool ZoomAllowed() const; int Deadzone() const; @@ -88,6 +103,9 @@ class CCamera : public CComponent int CamType() const { return m_CamType; } void UpdateCamera(); + void ResetAutoSpecCamera(); + bool SpectatingPlayer() const { return m_IsSpectatingPlayer; } + bool CanUseAutoSpecCamera() const; private: static void ConZoomPlus(IConsole::IResult *pResult, void *pUserData); diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index a4bb1a64125..65dabd7e0a2 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -587,16 +587,68 @@ void CHud::RenderTeambalanceWarning() void CHud::RenderCursor() { - if(!m_pClient->m_Snap.m_pLocalCharacter || Client()->State() == IClient::STATE_DEMOPLAYBACK) + if(Client()->State() != IClient::STATE_DEMOPLAYBACK && m_pClient->m_Snap.m_pLocalCharacter) + { + // render local cursor + int CurWeapon = maximum(0, m_pClient->m_Snap.m_pLocalCharacter->m_Weapon % NUM_WEAPONS); + vec2 TargetPos = m_pClient->m_Controls.m_aTargetPos[g_Config.m_ClDummy]; + + RenderTools()->MapScreenToInterface(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y); + Graphics()->SetColor(1.f, 1.f, 1.f, 1.f); + Graphics()->TextureSet(m_pClient->m_GameSkin.m_aSpriteWeaponCursors[CurWeapon]); + Graphics()->RenderQuadContainerAsSprite(m_HudQuadContainerIndex, m_aCursorOffset[CurWeapon], TargetPos.x, TargetPos.y); + return; + } + + if(!g_Config.m_ClSpecCursor || !m_pClient->m_CursorInfo.IsAvailable()) return; - RenderTools()->MapScreenToInterface(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y); + bool RenderSpecCursor = (m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_SpectatorId != SPEC_FREEVIEW) || Client()->State() == IClient::STATE_DEMOPLAYBACK; - // render cursor - int CurWeapon = maximum(0, m_pClient->m_Snap.m_pLocalCharacter->m_Weapon % NUM_WEAPONS); - Graphics()->SetColor(1.f, 1.f, 1.f, 1.f); + if(!RenderSpecCursor) + return; + + int CurWeapon = maximum(0, m_pClient->m_CursorInfo.Weapon() % NUM_WEAPONS); + vec2 TargetPos = m_pClient->m_CursorInfo.WorldTarget(); + + float CenterX = m_pClient->m_Camera.m_Center.x; + float CenterY = m_pClient->m_Camera.m_Center.y; + float Zoom = m_pClient->m_Camera.m_Zoom; + + float aPoints[4]; + RenderTools()->MapScreenToWorld(CenterX, CenterY, 100.0f, 100.0f, 100.0f, 0, 0, Graphics()->ScreenAspect(), Zoom, aPoints); + Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]); + + vec2 ScreenPos = TargetPos - m_pClient->m_Camera.m_Center; + + bool Clamped = false; + float HalfWidth = CenterX - aPoints[0]; + float HalfHeight = CenterY - aPoints[1]; + + // specialized lineseg-rect intersection + // https://gist.github.com/ChickenProp/3194723 + if(ScreenPos.x < -HalfWidth || ScreenPos.x > HalfWidth || ScreenPos.y < -HalfHeight || ScreenPos.y > HalfHeight) + { + float aDeltas[] = {ScreenPos.x, ScreenPos.y}; + float aBounds[] = {HalfWidth, HalfHeight}; + float ClampFactor = INFINITY; + + static_assert(std::size(aDeltas) == std::size(aBounds), "delta and bounds arrays must have the same size"); + for(std::size_t i = 0; i < std::size(aDeltas); i++) + { + float t = absolute(aBounds[i] / aDeltas[i]); + if(ClampFactor > t) + ClampFactor = t; + } + + Clamped = true; + TargetPos = ScreenPos * ClampFactor + m_pClient->m_Camera.m_Center; + } + + // render spec cursor + Graphics()->SetColor(1.f, 1.f, 1.f, Clamped ? .5f : 1.f); Graphics()->TextureSet(m_pClient->m_GameSkin.m_aSpriteWeaponCursors[CurWeapon]); - Graphics()->RenderQuadContainerAsSprite(m_HudQuadContainerIndex, m_aCursorOffset[CurWeapon], m_pClient->m_Controls.m_aTargetPos[g_Config.m_ClDummy].x, m_pClient->m_Controls.m_aTargetPos[g_Config.m_ClDummy].y); + Graphics()->RenderQuadContainerAsSprite(m_HudQuadContainerIndex, m_aCursorOffset[CurWeapon], TargetPos.x, TargetPos.y, Zoom, Zoom); } void CHud::PrepareAmmoHealthAndArmorQuads() @@ -1415,6 +1467,27 @@ void CHud::RenderSpectatorHud() str_copy(aBuf, Localize("Free-View")); } TextRender()->Text(m_Width - 174.0f, m_Height - 15.0f + (15.f - 8.f) / 2.f, 8.0f, aBuf, -1.0f); + + // draw the camera info + if(m_pClient->m_Camera.SpectatingPlayer() && m_pClient->m_Camera.CanUseAutoSpecCamera()) + { + bool AutoSpecCameraEnabled = m_pClient->m_Camera.m_AutoSpecCamera; + const char *pLabelText = Localize("AUTO", "Spectating Camera Mode Icon"); + const float TextWidth = TextRender()->TextWidth(6.0f, pLabelText); + + constexpr float RightMargin = 4.0f; + constexpr float IconWidth = 6.0f; + constexpr float Padding = 3.0f; + const float TagWidth = IconWidth + TextWidth + Padding * 3.0f; + const float TagX = m_Width - RightMargin - TagWidth; + Graphics()->DrawRect(TagX, m_Height - 12.0f, TagWidth, 10.0f, ColorRGBA(0.84f, 0.53f, 0.17f, AutoSpecCameraEnabled ? 0.85f : 0.25f), IGraphics::CORNER_ALL, 2.5f); + TextRender()->TextColor(1, 1, 1, AutoSpecCameraEnabled ? 1.0f : 0.65f); + TextRender()->SetFontPreset(EFontPreset::ICON_FONT); + TextRender()->Text(TagX + Padding, m_Height - 10.0f, 6.0f, FontIcons::FONT_ICON_CAMERA, -1.0f); + TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); + TextRender()->Text(TagX + Padding + IconWidth + Padding, m_Height - 10.0f, 6.0f, pLabelText, -1.0f); + TextRender()->TextColor(1, 1, 1, 1); + } } void CHud::RenderLocalTime(float x) diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index dd671c14227..6a81597bcb6 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -714,6 +714,19 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) } GameClient()->m_Tooltips.DoToolTip(&s_KeyboardShortcutsButton, &Button, Localize("Toggle keyboard shortcuts")); + // auto camera button (only available when it is possible to use) + if(m_pClient->m_Camera.CanUseAutoSpecCamera()) + { + ButtonBar.VSplitRight(Margins, &ButtonBar, nullptr); + ButtonBar.VSplitRight(ButtonbarHeight, &ButtonBar, &Button); + static CButtonContainer s_AutoCameraButton; + if(DoButton_FontIcon(&s_AutoCameraButton, FONT_ICON_CAMERA, 0, &Button, IGraphics::CORNER_ALL, m_pClient->m_Camera.m_AutoSpecCamera)) + { + m_pClient->m_Camera.m_AutoSpecCamera = !m_pClient->m_Camera.m_AutoSpecCamera; + } + GameClient()->m_Tooltips.DoToolTip(&s_AutoCameraButton, &Button, Localize("Toggle auto camera")); + } + // demo name char aDemoName[IO_MAX_PATH_LENGTH]; DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName)); diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index b8f8f7823bd..6be96b85ed1 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -3287,7 +3287,7 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView) Right.HSplitTop(20.0f, &Button, &Right); if(Ui()->DoScrollbarOption(&g_Config.m_ClDefaultZoom, &g_Config.m_ClDefaultZoom, &Button, Localize("Default zoom"), 0, 20)) - m_pClient->m_Camera.SetZoom(std::pow(CCamera::ZOOM_STEP, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); + m_pClient->m_Camera.SetZoom(CCamera::ZoomStepsToValue(g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime, true); Right.HSplitTop(20.0f, &Button, &Right); if(DoButton_CheckBox(&g_Config.m_ClAntiPing, Localize("AntiPing"), g_Config.m_ClAntiPing, &Button)) diff --git a/src/game/client/components/spectator.cpp b/src/game/client/components/spectator.cpp index 8240361c9e8..73deb3d249f 100644 --- a/src/game/client/components/spectator.cpp +++ b/src/game/client/components/spectator.cpp @@ -187,6 +187,15 @@ bool CSpectator::OnInput(const IInput::CEvent &Event) } } + if(m_pClient->m_Camera.SpectatingPlayer() && m_pClient->m_Camera.CanUseAutoSpecCamera()) + { + if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_MOUSE_2) + { + m_pClient->m_Camera.ResetAutoSpecCamera(); + return true; + } + } + return false; } diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index c49bfcd1401..a5c8ebf15d4 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -673,6 +673,9 @@ void CGameClient::OnReset() m_MultiViewActivated = false; m_MultiView.m_IsInit = false; + m_CursorInfo.m_CursorOwnerId = -1; + m_CursorInfo.m_NumSamples = 0; + for(auto &pComponent : m_vpAll) pComponent->OnReset(); @@ -782,6 +785,8 @@ void CGameClient::OnRender() // update camera data prior to CControls::OnRender to allow CControls::m_aTargetPos to compensate using camera data m_Camera.UpdateCamera(); + UpdateSpectatorCursor(); + // render all systems for(auto &pComponent : m_vpAll) pComponent->OnRender(); @@ -1498,6 +1503,7 @@ void CGameClient::InvalidateSnapshot() { // clear all pointers mem_zero(&m_Snap, sizeof(m_Snap)); + m_Snap.m_SpecInfo.m_Zoom = 1.0f; m_Snap.m_LocalClientId = -1; SnapCollectEntities(); } @@ -1757,6 +1763,14 @@ void CGameClient::OnNewSnapshot() m_Snap.m_SpecInfo.m_Active = true; m_Snap.m_SpecInfo.m_SpectatorId = m_Snap.m_pSpectatorInfo->m_SpectatorId; } + else if(Item.m_Type == NETOBJTYPE_DDNETSPECTATORINFO) + { + const CNetObj_DDNetSpectatorInfo *pDDNetSpecInfo = (const CNetObj_DDNetSpectatorInfo *)Item.m_pData; + m_Snap.m_SpecInfo.m_HasCameraInfo = true; + m_Snap.m_SpecInfo.m_Zoom = pDDNetSpecInfo->m_Zoom / 1000.0f; + m_Snap.m_SpecInfo.m_Deadzone = pDDNetSpecInfo->m_Deadzone; + m_Snap.m_SpecInfo.m_FollowFactor = pDDNetSpecInfo->m_FollowFactor; + } else if(Item.m_Type == NETOBJTYPE_GAMEINFO) { m_Snap.m_pGameInfoObj = (const CNetObj_GameInfo *)Item.m_pData; @@ -2066,6 +2080,14 @@ void CGameClient::OnNewSnapshot() float Deadzone = m_Camera.Deadzone(); float FollowFactor = m_Camera.FollowFactor(); + if(m_Snap.m_SpecInfo.m_Active) + { + // don't send camera infomation when spectating + Zoom = m_LastZoom; + Deadzone = m_LastDeadzone; + FollowFactor = m_LastFollowFactor; + } + // initialize dummy vital when first connected if(Client()->DummyConnected() && !m_LastDummyConnected) { @@ -3094,6 +3116,131 @@ void CGameClient::UpdatePrediction() m_GameWorld.NetObjEnd(); } +void CGameClient::UpdateSpectatorCursor() +{ + int CursorOwnerId = m_Snap.m_LocalClientId; + if(m_Snap.m_SpecInfo.m_Active || Client()->State() == IClient::STATE_DEMOPLAYBACK) + { + CursorOwnerId = m_Snap.m_SpecInfo.m_SpectatorId; + } + + if(CursorOwnerId != m_CursorInfo.m_CursorOwnerId) + { + // reset cursor sample count upon changing spectating character + m_CursorInfo.m_NumSamples = 0; + m_CursorInfo.m_CursorOwnerId = CursorOwnerId; + } + + if(m_MultiViewActivated || CursorOwnerId < 0 || CursorOwnerId >= MAX_CLIENTS) + { + // do not show spec cursor in multi-view + m_CursorInfo.m_Available = false; + m_CursorInfo.m_NumSamples = 0; + return; + } + + const CSnapState::CCharacterInfo CharInfo = m_Snap.m_aCharacters[CursorOwnerId]; + if(!CharInfo.m_HasExtendedData || !m_aClients[CursorOwnerId].m_Active || (!g_Config.m_Debug && m_aClients[CursorOwnerId].m_Paused)) + { + // hide cursor when the spectating player is paused + m_CursorInfo.m_Available = false; + m_CursorInfo.m_NumSamples = 0; + return; + } + + m_CursorInfo.m_Available = true; + m_CursorInfo.m_Position = CharInfo.m_Position; + m_CursorInfo.m_Weapon = CharInfo.m_Cur.m_Weapon; + + const vec2 Target = vec2(CharInfo.m_ExtendedData.m_TargetX, CharInfo.m_ExtendedData.m_TargetY); + + // interpolate cursor positions when not in debug mode + const double Tick = Client()->GameTick(g_Config.m_ClDummy); + + const bool HasSample = m_CursorInfo.m_NumSamples > 0; + const vec2 LastInput = HasSample ? m_CursorInfo.m_aTargetSamplesData[m_CursorInfo.m_NumSamples - 1] : vec2(0.0f, 0.0f); + const double LastTime = HasSample ? m_CursorInfo.m_aTargetSamplesTime[m_CursorInfo.m_NumSamples - 1] : 0.0; + bool NewSample = LastInput != Target || LastTime + CCursorInfo::REST_THRESHOLD < Tick; + + if(LastTime > Tick) + { + // clear samples when time flows backwards + m_CursorInfo.m_NumSamples = 0; + NewSample = true; + } + + if(m_CursorInfo.m_NumSamples == 0) + { + m_CursorInfo.m_aTargetSamplesTime[0] = Tick - CCursorInfo::INTERP_DELAY; + m_CursorInfo.m_aTargetSamplesData[0] = Target; + } + + if(NewSample) + { + if(m_CursorInfo.m_NumSamples == CCursorInfo::CURSOR_SAMPLES) + { + m_CursorInfo.m_NumSamples--; + mem_move(m_CursorInfo.m_aTargetSamplesTime, m_CursorInfo.m_aTargetSamplesTime + 1, m_CursorInfo.m_NumSamples * sizeof(double)); + mem_move(m_CursorInfo.m_aTargetSamplesData, m_CursorInfo.m_aTargetSamplesData + 1, m_CursorInfo.m_NumSamples * sizeof(vec2)); + } + m_CursorInfo.m_aTargetSamplesTime[m_CursorInfo.m_NumSamples] = Tick; + m_CursorInfo.m_aTargetSamplesData[m_CursorInfo.m_NumSamples] = Target; + m_CursorInfo.m_NumSamples++; + } + + // using double to avoid precision loss when converting int tick to decimal type + const double DisplayTime = Tick - CCursorInfo::INTERP_DELAY + double(Client()->IntraGameTickSincePrev(g_Config.m_ClDummy)); + double aTime[CCursorInfo::SAMPLE_FRAME_WINDOW]; + vec2 aData[CCursorInfo::SAMPLE_FRAME_WINDOW]; + + // find the available sample timing + int Index = m_CursorInfo.m_NumSamples; + for(int i = 0; i < m_CursorInfo.m_NumSamples; i++) + { + if(m_CursorInfo.m_aTargetSamplesTime[i] > DisplayTime) + { + Index = i; + break; + } + } + + for(int i = 0; i < CCursorInfo::SAMPLE_FRAME_WINDOW; i++) + { + const int Offset = i - CCursorInfo::SAMPLE_FRAME_OFFSET; + const int SampleIndex = Index + Offset; + if(SampleIndex < 0) + { + aTime[i] = m_CursorInfo.m_aTargetSamplesTime[0] + CCursorInfo::REST_THRESHOLD * Offset; + aData[i] = m_CursorInfo.m_aTargetSamplesData[0]; + } + else if(SampleIndex >= m_CursorInfo.m_NumSamples) + { + aTime[i] = m_CursorInfo.m_aTargetSamplesTime[m_CursorInfo.m_NumSamples - 1] + CCursorInfo::REST_THRESHOLD * (Offset + 1); + aData[i] = m_CursorInfo.m_aTargetSamplesData[m_CursorInfo.m_NumSamples - 1]; + } + else + { + aTime[i] = m_CursorInfo.m_aTargetSamplesTime[SampleIndex]; + aData[i] = m_CursorInfo.m_aTargetSamplesData[SampleIndex]; + } + } + + m_CursorInfo.m_Target = mix_polynomial(aTime, aData, CCursorInfo::SAMPLE_FRAME_WINDOW, DisplayTime, vec2(0.0f, 0.0f)); + + vec2 TargetCameraOffset(0, 0); + float l = length(m_CursorInfo.m_Target); + + if(l > 0.0001f) // make sure that this isn't 0 + { + float OffsetAmount = maximum(l - m_Snap.m_SpecInfo.m_Deadzone, 0.0f) * (m_Snap.m_SpecInfo.m_FollowFactor / 100.0f); + TargetCameraOffset = normalize(m_CursorInfo.m_Target) * OffsetAmount; + } + + // if we are in auto spec mode, use camera zoom to smooth out cursor transitions + const float Zoom = (m_Camera.m_Zooming && m_Camera.m_AutoSpecCameraZooming) ? m_Camera.m_Zoom : m_Snap.m_SpecInfo.m_Zoom; + m_CursorInfo.m_WorldTarget = m_CursorInfo.m_Position + (m_CursorInfo.m_Target - TargetCameraOffset) * Zoom + TargetCameraOffset; +} + void CGameClient::UpdateRenderedCharacters() { for(int i = 0; i < MAX_CLIENTS; i++) @@ -4121,9 +4268,9 @@ void CGameClient::HandleMultiView() float AvgVel = clamp(TmpVel / AmountPlayers ? TmpVel / (float)AmountPlayers : 0.0f, 0.0f, 1000.0f); if(m_MultiView.m_OldPersonalZoom == m_MultiViewPersonalZoom) - m_Camera.SetZoom(CalculateMultiViewZoom(Minpos, Maxpos, AvgVel), g_Config.m_ClMultiViewZoomSmoothness); + m_Camera.SetZoom(CalculateMultiViewZoom(Minpos, Maxpos, AvgVel), g_Config.m_ClMultiViewZoomSmoothness, false); else - m_Camera.SetZoom(CalculateMultiViewZoom(Minpos, Maxpos, AvgVel), 50); + m_Camera.SetZoom(CalculateMultiViewZoom(Minpos, Maxpos, AvgVel), 50, false); m_Snap.m_SpecInfo.m_Position = m_MultiView.m_OldPos + ((TargetPos - m_MultiView.m_OldPos) * CalculateMultiViewMultiplier(TargetPos)); m_MultiView.m_OldPos = m_Snap.m_SpecInfo.m_Position; @@ -4280,7 +4427,7 @@ float CGameClient::CalculateMultiViewZoom(vec2 MinPos, vec2 MaxPos, float Vel) // zoom should stay between 1.1 and 20.0 Zoom = clamp(Zoom + Diff, 1.1f, 20.0f); // dont go below default zoom - Zoom = std::max(float(std::pow(CCamera::ZOOM_STEP, g_Config.m_ClDefaultZoom - 10)), Zoom); + Zoom = std::max(CCamera::ZoomStepsToValue(g_Config.m_ClDefaultZoom - 10), Zoom); // add the user preference Zoom -= (Zoom * 0.1f) * m_MultiViewPersonalZoom; m_MultiView.m_OldPersonalZoom = m_MultiViewPersonalZoom; @@ -4295,7 +4442,7 @@ float CGameClient::MapValue(float MaxValue, float MinValue, float MaxRange, floa void CGameClient::ResetMultiView() { - m_Camera.SetZoom(std::pow(CCamera::ZOOM_STEP, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); + m_Camera.SetZoom(CCamera::ZoomStepsToValue(g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime, true); m_MultiViewPersonalZoom = 0.0f; m_MultiViewActivated = false; m_MultiView.m_Solo = false; diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 6b42d95cb51..fc81bf5932c 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -332,12 +332,18 @@ class CGameClient : public IGameClient int m_HighestClientId; // spectate data - struct CSpectateInfo + class CSpectateInfo { + public: bool m_Active; int m_SpectatorId; bool m_UsePosition; vec2 m_Position; + + bool m_HasCameraInfo; + float m_Zoom; + int m_Deadzone; + int m_FollowFactor; } m_SpecInfo; // @@ -368,6 +374,35 @@ class CGameClient : public IGameClient int m_aExpectingTuningSince[NUM_DUMMIES]; CTuningParams m_aTuning[NUM_DUMMIES]; + // spectate cursor data + class CCursorInfo + { + friend class CGameClient; + static constexpr int CURSOR_SAMPLES = 8; // how many samples to keep + static constexpr int SAMPLE_FRAME_WINDOW = 3; // how many samples should be used for polynomial interpolation + static constexpr int SAMPLE_FRAME_OFFSET = 2; // how many samples in the past should be included + static constexpr double INTERP_DELAY = 4.25; // how many ticks in the past to show, enables extrapolation with smaller value (<= SAMPLE_FRAME_WINDOW - SAMPLE_FRAME_OFFSET + 3) + static constexpr double REST_THRESHOLD = 3.0; // how many ticks of the same samples are considered to be resting + + int m_CursorOwnerId; + double m_aTargetSamplesTime[CURSOR_SAMPLES]; + vec2 m_aTargetSamplesData[CURSOR_SAMPLES]; + int m_NumSamples; + + bool m_Available; + int m_Weapon; + vec2 m_Target; + vec2 m_WorldTarget; + vec2 m_Position; + + public: + bool IsAvailable() const { return m_Available; } + int Weapon() const { return m_Weapon; } + vec2 Target() const { return m_Target; } + vec2 WorldTarget() const { return m_WorldTarget; } + vec2 Position() const { return m_Position; } + } m_CursorInfo; + // client data struct CClientData { @@ -799,6 +834,7 @@ class CGameClient : public IGameClient int m_aShowOthers[NUM_DUMMIES]; void UpdatePrediction(); + void UpdateSpectatorCursor(); void UpdateRenderedCharacters(); int m_aLastUpdateTick[MAX_CLIENTS] = {0}; diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 9f280760796..2d2659c3ef3 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -413,6 +413,25 @@ void CPlayer::Snap(int SnappingClient) } } + if(m_ClientId == SnappingClient) + { + // send extended spectator info even when playing, this allows demo to record camera settings for local player + const int SpectatingClient = ((m_Team != TEAM_SPECTATORS && !m_Paused) || m_SpectatorId < 0 || m_SpectatorId >= MAX_CLIENTS) ? id : m_SpectatorId; + const CPlayer *pSpecPlayer = GameServer()->m_apPlayers[SpectatingClient]; + + if(pSpecPlayer) + { + CNetObj_DDNetSpectatorInfo *pDDNetSpectatorInfo = Server()->SnapNewItem(id); + if(!pDDNetSpectatorInfo) + return; + + pDDNetSpectatorInfo->m_HasCameraInfo = pSpecPlayer->m_CameraInfo.m_HasCameraInfo; + pDDNetSpectatorInfo->m_Zoom = pSpecPlayer->m_CameraInfo.m_Zoom * 1000.0f; + pDDNetSpectatorInfo->m_Deadzone = pSpecPlayer->m_CameraInfo.m_Deadzone; + pDDNetSpectatorInfo->m_FollowFactor = pSpecPlayer->m_CameraInfo.m_FollowFactor; + } + } + CNetObj_DDNetPlayer *pDDNetPlayer = Server()->SnapNewItem(id); if(!pDDNetPlayer) return; @@ -973,6 +992,7 @@ vec2 CPlayer::CCameraInfo::ConvertTargetToWorld(vec2 Position, vec2 Target) cons void CPlayer::CCameraInfo::Write(const CNetMsg_Cl_CameraInfo *Msg) { + m_HasCameraInfo = true; m_Zoom = Msg->m_Zoom / 1000.0f; m_Deadzone = Msg->m_Deadzone; m_FollowFactor = Msg->m_FollowFactor; @@ -980,6 +1000,7 @@ void CPlayer::CCameraInfo::Write(const CNetMsg_Cl_CameraInfo *Msg) void CPlayer::CCameraInfo::Reset() { + m_HasCameraInfo = false; m_Zoom = 1.0f; m_Deadzone = 0.0f; m_FollowFactor = 0.0f; diff --git a/src/game/server/player.h b/src/game/server/player.h index 5703a0a765c..52c6f8e9ec5 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -194,6 +194,8 @@ class CPlayer // camera info is used sparingly for converting aim target to absolute world coordinates class CCameraInfo { + friend class CPlayer; + bool m_HasCameraInfo; float m_Zoom; int m_Deadzone; int m_FollowFactor; From f401fc2c06f48ae2c2fa81110fb3084f53b95ad1 Mon Sep 17 00:00:00 2001 From: Dennis Felsing Date: Thu, 19 Dec 2024 14:47:09 +0100 Subject: [PATCH 33/40] Add tests for fuzzy case-insensitive matching in /map and /mapinfo --- src/test/score.cpp | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/test/score.cpp b/src/test/score.cpp index d24b52fb1dc..9099eca3caf 100644 --- a/src/test/score.cpp +++ b/src/test/score.cpp @@ -42,7 +42,7 @@ struct Score : public testing::TestWithParam { Connect(); LoadBestTime(); - InsertMap(); + InsertMap("Kobra 3", "Zerodin", "Novice", 5, 5); } ~Score() @@ -75,15 +75,15 @@ struct Score : public testing::TestWithParam ASSERT_FALSE(CScoreWorker::LoadBestTime(m_pConn, &loadBestTimeReq, m_aError, sizeof(m_aError))) << m_aError; } - void InsertMap() + void InsertMap(const char *pName, const char *pMapper, const char *pServer, int Points, int Stars) { char aTimestamp[32]; str_timestamp_format(aTimestamp, sizeof(aTimestamp), FORMAT_SPACE); char aBuf[512]; str_format(aBuf, sizeof(aBuf), "%s into %s_maps(Map, Server, Mapper, Points, Stars, Timestamp) " - "VALUES (\"Kobra 3\", \"Novice\", \"Zerodin\", 5, 5, %s)", - m_pConn->InsertIgnore(), m_pConn->GetPrefix(), m_pConn->InsertTimestampAsUtc()); + "VALUES (\"%s\", \"%s\", \"%s\", %d, %d, %s)", + m_pConn->InsertIgnore(), m_pConn->GetPrefix(), pName, pServer, pMapper, Points, Stars, m_pConn->InsertTimestampAsUtc()); ASSERT_FALSE(m_pConn->PrepareStatement(aBuf, m_aError, sizeof(m_aError))) << m_aError; m_pConn->BindString(1, aTimestamp); int NumInserted = 0; @@ -418,6 +418,21 @@ TEST_P(MapInfo, Fuzzy) } } +TEST_P(MapInfo, FuzzyCase) +{ + InsertMap("Reflect", "DarkOort", "Dummy", 20, 3); + InsertMap("reflects", "Ninjed & Pipou", "Solo", 16, 4); + str_copy(m_PlayerRequest.m_aName, "reflect", sizeof(m_PlayerRequest.m_aName)); + ASSERT_FALSE(CScoreWorker::MapInfo(m_pConn, &m_PlayerRequest, m_aError, sizeof(m_aError))) << m_aError; + + EXPECT_EQ(m_pPlayerResult->m_MessageKind, CScorePlayerResult::DIRECT); + EXPECT_THAT(m_pPlayerResult->m_Data.m_aaMessages[0], testing::MatchesRegex("\"Reflect\" by DarkOort on Dummy, ★★★✰✰, 20 points, released .* ago, 0 finishes by 0 tees")); + for(int i = 1; i < CScorePlayerResult::MAX_MESSAGES; i++) + { + EXPECT_STREQ(m_pPlayerResult->m_Data.m_aaMessages[i], ""); + } +} + TEST_P(MapInfo, DoesntExit) { str_copy(m_PlayerRequest.m_aName, "f", sizeof(m_PlayerRequest.m_aName)); @@ -453,6 +468,18 @@ TEST_P(MapVote, Fuzzy) EXPECT_STREQ(m_pPlayerResult->m_Data.m_MapVote.m_aServer, "novice"); } +TEST_P(MapVote, FuzzyCase) +{ + InsertMap("Reflect", "DarkOort", "Dummy", 20, 3); + InsertMap("reflects", "Ninjed & Pipou", "Solo", 16, 4); + str_copy(m_PlayerRequest.m_aName, "reflect", sizeof(m_PlayerRequest.m_aName)); + ASSERT_FALSE(CScoreWorker::MapVote(m_pConn, &m_PlayerRequest, m_aError, sizeof(m_aError))) << m_aError; + EXPECT_EQ(m_pPlayerResult->m_MessageKind, CScorePlayerResult::MAP_VOTE); + EXPECT_STREQ(m_pPlayerResult->m_Data.m_MapVote.m_aMap, "Reflect"); + EXPECT_STREQ(m_pPlayerResult->m_Data.m_MapVote.m_aReason, "/map"); + EXPECT_STREQ(m_pPlayerResult->m_Data.m_MapVote.m_aServer, "dummy"); +} + TEST_P(MapVote, DoesntExist) { str_copy(m_PlayerRequest.m_aName, "f", sizeof(m_PlayerRequest.m_aName)); From 6445af5976664f7f2e8b18eb1fae18ae6cdb7cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 16 Dec 2024 21:53:31 +0100 Subject: [PATCH 34/40] Refactor state based key event handling, fix controller input Remove the `CInput::UpdateMouseState` and `CInput::UpdateJoystickState` functions and the usages of the `SDL_PumpEvents` and `SDL_GetKeyboardState` functions. Instead of unnecessarily clearing and then restoring the key state at the beginning of every input update we now update the key states consistently when handling each key press/release event (keyboard, mouse click, mouse wheel, joystick button, joystick axis, joystick hat). Fixes binds for joystick axes and hats not working consistently as calling `SDL_PumpEvents` seems to cause most joystick events to be consumed already. Closes #9381. Replace the `CInput::KeyState` function with the `IInput::KeyIsPressed` function which was previously wrapping it. Remove the unused parameter `bool CheckCount = false` of the `IInput::KeyPress` function which was always set to its default value. Use `bool` instead of storing the therefore unused input count value. Rename `CInput::m_aInputState` and `CInput::m_aInputCount` variables to `m_aCurrentKeyStates` and `m_aFrameKeyStates`, respectively, to make their usage more understandable. Add documentation for the `IInput::KeyIsPressed` and `IInput::KeyPress` functions to explain the difference in behavior and usage. Add assertions to ensure keys are valid. --- src/engine/client/input.cpp | 144 ++++++++++++------------------------ src/engine/client/input.h | 19 ++--- src/engine/input.h | 32 ++++++-- 3 files changed, 84 insertions(+), 111 deletions(-) diff --git a/src/engine/client/input.cpp b/src/engine/client/input.cpp index cb4ec78b503..d5e0be74e15 100644 --- a/src/engine/client/input.cpp +++ b/src/engine/client/input.cpp @@ -32,16 +32,41 @@ // for platform specific features that aren't available or are broken in SDL #include +#ifdef KeyPress +#undef KeyPress // Undo pollution from X11/Xlib.h included by SDL_syswm.h on Linux +#endif + +static void AssertKeyValid(int Key) +{ + if(Key < KEY_FIRST || Key >= KEY_LAST) + { + char aError[32]; + str_format(aError, sizeof(aError), "Key invalid: %d", Key); + dbg_assert(false, aError); + } +} void CInput::AddKeyEvent(int Key, int Flags) { + AssertKeyValid(Key); dbg_assert((Flags & (FLAG_PRESS | FLAG_RELEASE)) != 0 && (Flags & ~(FLAG_PRESS | FLAG_RELEASE)) == 0, "Flags invalid"); + CEvent Event; Event.m_Key = Key; Event.m_Flags = Flags; Event.m_aText[0] = '\0'; Event.m_InputCount = m_InputCounter; m_vInputEvents.emplace_back(Event); + + if(Flags & IInput::FLAG_PRESS) + { + m_aCurrentKeyStates[Key] = true; + m_aFrameKeyStates[Key] = true; + } + if(Flags & IInput::FLAG_RELEASE) + { + m_aCurrentKeyStates[Key] = false; + } } void CInput::AddTextEvent(const char *pText) @@ -56,8 +81,8 @@ void CInput::AddTextEvent(const char *pText) CInput::CInput() { - mem_zero(m_aInputCount, sizeof(m_aInputCount)); - mem_zero(m_aInputState, sizeof(m_aInputState)); + std::fill(std::begin(m_aCurrentKeyStates), std::end(m_aCurrentKeyStates), false); + std::fill(std::begin(m_aFrameKeyStates), std::end(m_aFrameKeyStates), false); m_vInputEvents.reserve(32); m_LastUpdate = 0; @@ -353,8 +378,7 @@ void CInput::ConsumeEvents(std::function Consumer) co void CInput::Clear() { - mem_zero(m_aInputState, sizeof(m_aInputState)); - mem_zero(m_aInputCount, sizeof(m_aInputCount)); + std::fill(std::begin(m_aFrameKeyStates), std::end(m_aFrameKeyStates), false); m_vInputEvents.clear(); ClearTouchDeltas(); } @@ -364,11 +388,22 @@ float CInput::GetUpdateTime() const return m_UpdateTime; } -bool CInput::KeyState(int Key) const +bool CInput::KeyIsPressed(int Key) const { - if(Key < KEY_FIRST || Key >= KEY_LAST) - return false; - return m_aInputState[Key]; + AssertKeyValid(Key); + return m_aCurrentKeyStates[Key]; +} + +bool CInput::KeyPress(int Key) const +{ + AssertKeyValid(Key); + return m_aFrameKeyStates[Key]; +} + +const char *CInput::KeyName(int Key) const +{ + AssertKeyValid(Key); + return g_aaKeyStrings[Key]; } int CInput::FindKeyByName(const char *pKeyName) const @@ -391,56 +426,6 @@ int CInput::FindKeyByName(const char *pKeyName) const return KEY_UNKNOWN; } -void CInput::UpdateMouseState() -{ - const int MouseState = SDL_GetMouseState(nullptr, nullptr); - if(MouseState & SDL_BUTTON(SDL_BUTTON_LEFT)) - m_aInputState[KEY_MOUSE_1] = 1; - if(MouseState & SDL_BUTTON(SDL_BUTTON_RIGHT)) - m_aInputState[KEY_MOUSE_2] = 1; - if(MouseState & SDL_BUTTON(SDL_BUTTON_MIDDLE)) - m_aInputState[KEY_MOUSE_3] = 1; - if(MouseState & SDL_BUTTON(SDL_BUTTON_X1)) - m_aInputState[KEY_MOUSE_4] = 1; - if(MouseState & SDL_BUTTON(SDL_BUTTON_X2)) - m_aInputState[KEY_MOUSE_5] = 1; - if(MouseState & SDL_BUTTON(6)) - m_aInputState[KEY_MOUSE_6] = 1; - if(MouseState & SDL_BUTTON(7)) - m_aInputState[KEY_MOUSE_7] = 1; - if(MouseState & SDL_BUTTON(8)) - m_aInputState[KEY_MOUSE_8] = 1; - if(MouseState & SDL_BUTTON(9)) - m_aInputState[KEY_MOUSE_9] = 1; -} - -void CInput::UpdateJoystickState() -{ - if(!g_Config.m_InpControllerEnable) - return; - IJoystick *pJoystick = GetActiveJoystick(); - if(!pJoystick) - return; - - const float DeadZone = GetJoystickDeadzone(); - for(int Axis = 0; Axis < pJoystick->GetNumAxes(); Axis++) - { - const float Value = pJoystick->GetAxisValue(Axis); - const int LeftKey = KEY_JOY_AXIS_0_LEFT + 2 * Axis; - const int RightKey = LeftKey + 1; - m_aInputState[LeftKey] = Value <= -DeadZone; - m_aInputState[RightKey] = Value >= DeadZone; - } - - for(int Hat = 0; Hat < pJoystick->GetNumHats(); Hat++) - { - int HatKeys[2]; - pJoystick->GetHatValue(Hat, HatKeys); - for(int Key = KEY_JOY_HAT0_UP + Hat * NUM_JOYSTICK_BUTTONS_PER_HAT; Key <= KEY_JOY_HAT0_DOWN + Hat * NUM_JOYSTICK_BUTTONS_PER_HAT; Key++) - m_aInputState[Key] = HatKeys[0] == Key || HatKeys[1] == Key; - } -} - void CInput::HandleJoystickAxisMotionEvent(const SDL_JoyAxisEvent &Event) { if(!g_Config.m_InpControllerEnable) @@ -455,27 +440,21 @@ void CInput::HandleJoystickAxisMotionEvent(const SDL_JoyAxisEvent &Event) const int RightKey = LeftKey + 1; const float DeadZone = GetJoystickDeadzone(); - if(Event.value <= SDL_JOYSTICK_AXIS_MIN * DeadZone && !m_aInputState[LeftKey]) + if(Event.value <= SDL_JOYSTICK_AXIS_MIN * DeadZone && !m_aCurrentKeyStates[LeftKey]) { - m_aInputState[LeftKey] = true; - m_aInputCount[LeftKey] = m_InputCounter; AddKeyEvent(LeftKey, IInput::FLAG_PRESS); } - else if(Event.value > SDL_JOYSTICK_AXIS_MIN * DeadZone && m_aInputState[LeftKey]) + else if(Event.value > SDL_JOYSTICK_AXIS_MIN * DeadZone && m_aCurrentKeyStates[LeftKey]) { - m_aInputState[LeftKey] = false; AddKeyEvent(LeftKey, IInput::FLAG_RELEASE); } - if(Event.value >= SDL_JOYSTICK_AXIS_MAX * DeadZone && !m_aInputState[RightKey]) + if(Event.value >= SDL_JOYSTICK_AXIS_MAX * DeadZone && !m_aCurrentKeyStates[RightKey]) { - m_aInputState[RightKey] = true; - m_aInputCount[RightKey] = m_InputCounter; AddKeyEvent(RightKey, IInput::FLAG_PRESS); } - else if(Event.value < SDL_JOYSTICK_AXIS_MAX * DeadZone && m_aInputState[RightKey]) + else if(Event.value < SDL_JOYSTICK_AXIS_MAX * DeadZone && m_aCurrentKeyStates[RightKey]) { - m_aInputState[RightKey] = false; AddKeyEvent(RightKey, IInput::FLAG_RELEASE); } } @@ -494,13 +473,10 @@ void CInput::HandleJoystickButtonEvent(const SDL_JoyButtonEvent &Event) if(Event.type == SDL_JOYBUTTONDOWN) { - m_aInputState[Key] = true; - m_aInputCount[Key] = m_InputCounter; AddKeyEvent(Key, IInput::FLAG_PRESS); } else if(Event.type == SDL_JOYBUTTONUP) { - m_aInputState[Key] = false; AddKeyEvent(Key, IInput::FLAG_RELEASE); } } @@ -520,19 +496,16 @@ void CInput::HandleJoystickHatMotionEvent(const SDL_JoyHatEvent &Event) for(int Key = KEY_JOY_HAT0_UP + Event.hat * NUM_JOYSTICK_BUTTONS_PER_HAT; Key <= KEY_JOY_HAT0_DOWN + Event.hat * NUM_JOYSTICK_BUTTONS_PER_HAT; Key++) { - if(Key != HatKeys[0] && Key != HatKeys[1] && m_aInputState[Key]) + if(Key != HatKeys[0] && Key != HatKeys[1] && m_aCurrentKeyStates[Key]) { - m_aInputState[Key] = false; AddKeyEvent(Key, IInput::FLAG_RELEASE); } } for(int CurrentKey : HatKeys) { - if(CurrentKey != KEY_UNKNOWN && !m_aInputState[CurrentKey]) + if(CurrentKey != KEY_UNKNOWN && !m_aCurrentKeyStates[CurrentKey]) { - m_aInputState[CurrentKey] = true; - m_aInputCount[CurrentKey] = m_InputCounter; AddKeyEvent(CurrentKey, IInput::FLAG_PRESS); } } @@ -674,20 +647,6 @@ int CInput::Update() // keep the counter between 1..0xFFFFFFFF, 0 means not pressed m_InputCounter = (m_InputCounter % std::numeric_limits::max()) + 1; - // Ensure that we have the latest keyboard, mouse and joystick state - SDL_PumpEvents(); - - int NumKeyStates; - const Uint8 *pState = SDL_GetKeyboardState(&NumKeyStates); - if(NumKeyStates >= KEY_MOUSE_1) - NumKeyStates = KEY_MOUSE_1; - mem_copy(m_aInputState, pState, NumKeyStates); - mem_zero(m_aInputState + NumKeyStates, KEY_LAST - NumKeyStates); - - // these states must always be updated manually because they are not in the SDL_GetKeyboardState from SDL - UpdateMouseState(); - UpdateJoystickState(); - SDL_Event Event; bool IgnoreKeys = false; while(SDL_PollEvent(&Event)) @@ -861,11 +820,6 @@ int CInput::Update() if(Scancode > KEY_FIRST && Scancode < g_MaxKeys && !IgnoreKeys && !HasComposition()) { - if(Action & IInput::FLAG_PRESS) - { - m_aInputState[Scancode] = 1; - m_aInputCount[Scancode] = m_InputCounter; - } AddKeyEvent(Scancode, Action); } } diff --git a/src/engine/client/input.h b/src/engine/client/input.h index 7d491571e68..e240938b5e1 100644 --- a/src/engine/client/input.h +++ b/src/engine/client/input.h @@ -93,13 +93,11 @@ class CInput : public IEngineInput void AddTextEvent(const char *pText); // quick access to input - uint32_t m_aInputCount[g_MaxKeys]; - unsigned char m_aInputState[g_MaxKeys]; + bool m_aCurrentKeyStates[KEY_LAST]; + bool m_aFrameKeyStates[KEY_LAST]; uint32_t m_InputCounter; std::vector m_vTouchFingerStates; - void UpdateMouseState(); - void UpdateJoystickState(); void HandleJoystickAxisMotionEvent(const SDL_JoyAxisEvent &Event); void HandleJoystickButtonEvent(const SDL_JoyButtonEvent &Event); void HandleJoystickHatMotionEvent(const SDL_JoyHatEvent &Event); @@ -112,8 +110,6 @@ class CInput : public IEngineInput char m_aDropFile[IO_MAX_PATH_LENGTH]; - bool KeyState(int Key) const; - void ProcessSystemMessage(SDL_SysWMmsg *pMsg); public: @@ -127,11 +123,12 @@ class CInput : public IEngineInput void Clear() override; float GetUpdateTime() const override; - bool ModifierIsPressed() const override { return KeyState(KEY_LCTRL) || KeyState(KEY_RCTRL) || KeyState(KEY_LGUI) || KeyState(KEY_RGUI); } - bool ShiftIsPressed() const override { return KeyState(KEY_LSHIFT) || KeyState(KEY_RSHIFT); } - bool AltIsPressed() const override { return KeyState(KEY_LALT) || KeyState(KEY_RALT); } - bool KeyIsPressed(int Key) const override { return KeyState(Key); } - bool KeyPress(int Key, bool CheckCounter) const override { return CheckCounter ? (m_aInputCount[Key] == m_InputCounter) : m_aInputCount[Key]; } + bool ModifierIsPressed() const override { return KeyIsPressed(KEY_LCTRL) || KeyIsPressed(KEY_RCTRL) || KeyIsPressed(KEY_LGUI) || KeyIsPressed(KEY_RGUI); } + bool ShiftIsPressed() const override { return KeyIsPressed(KEY_LSHIFT) || KeyIsPressed(KEY_RSHIFT); } + bool AltIsPressed() const override { return KeyIsPressed(KEY_LALT) || KeyIsPressed(KEY_RALT); } + bool KeyIsPressed(int Key) const override; + bool KeyPress(int Key) const override; + const char *KeyName(int Key) const override; int FindKeyByName(const char *pKeyName) const override; size_t NumJoysticks() const override { return m_vJoysticks.size(); } diff --git a/src/engine/input.h b/src/engine/input.h index 873423b8085..86eb20d6552 100644 --- a/src/engine/input.h +++ b/src/engine/input.h @@ -14,9 +14,6 @@ #include #include -const int g_MaxKeys = 512; -extern const char g_aaKeyStrings[g_MaxKeys][20]; - class IInput : public IInterface { MACRO_INTERFACE("input") @@ -45,6 +42,9 @@ class IInput : public IInterface // events virtual void ConsumeEvents(std::function Consumer) const = 0; + /** + * Clears the events and @link KeyPress @endlink state for this frame. Must be called at the end of each frame. + */ virtual void Clear() = 0; /** @@ -57,9 +57,31 @@ class IInput : public IInterface virtual bool ModifierIsPressed() const = 0; virtual bool ShiftIsPressed() const = 0; virtual bool AltIsPressed() const = 0; + /** + * Returns whether the given key is currently pressed down. This directly represents the state of pressed keys + * based on all handled input events. + * + * This function should be used to trigger behavior continuously while a specific key is held down, e.g. for + * showing a list of all keys that are currently being pressed. + * + * @param Key The key code (see `keys.h`). + * + * @return `true` if key is currently pressed down, `false` otherwise. + */ virtual bool KeyIsPressed(int Key) const = 0; - virtual bool KeyPress(int Key, bool CheckCounter = false) const = 0; - const char *KeyName(int Key) const { return (Key >= 0 && Key < g_MaxKeys) ? g_aaKeyStrings[Key] : g_aaKeyStrings[0]; } + /** + * Returns whether the given key was pressed down during input updates for current frame. This state is + * cleared at the end of each frame by calling the @link Clear @endlink function. + * + * This function should be used to trigger behavior only once per key press event per frame, e.g. for menu + * hotkeys that should activate behavior once per key press. + * + * @param Key The key code (see `keys.h`). + * + * @return `true` if key was pressed down during input updates for the current frame, `false` otherwise. + */ + virtual bool KeyPress(int Key) const = 0; + virtual const char *KeyName(int Key) const = 0; virtual int FindKeyByName(const char *pKeyName) const = 0; // joystick From edfd48fc9672a42d00c3607c814bab8c4c70b8c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 18 Dec 2024 22:11:25 +0100 Subject: [PATCH 35/40] Fix swapped mouse wheel left/right, refactor key event handling Fix the mouse wheel left and right scroll keys being swapped. Negative `x` is left and positive is right according to the documentation for `SDL_MouseButtonEvent`. Extract functions for translation of keys for mouse button and wheel events for readability. Improve readability by avoiding assignment to variables in switch-cases. Use lambda-function to avoid duplicate code instead. Consistently use name `Key` instead of `Scancode`. --- src/engine/client/input.cpp | 121 ++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 47 deletions(-) diff --git a/src/engine/client/input.cpp b/src/engine/client/input.cpp index d5e0be74e15..c7ce4fd082c 100644 --- a/src/engine/client/input.cpp +++ b/src/engine/client/input.cpp @@ -600,7 +600,7 @@ void CInput::SetCompositionWindowPosition(float X, float Y, float H) SDL_SetTextInputRect(&Rect); } -static int TranslateScancode(const SDL_KeyboardEvent &KeyEvent) +static int TranslateKeyEventKey(const SDL_KeyboardEvent &KeyEvent) { // See SDL_Keymod for possible modifiers: // NONE = 0 @@ -618,20 +618,71 @@ static int TranslateScancode(const SDL_KeyboardEvent &KeyEvent) // Sum if you want to ignore multiple modifiers. if(KeyEvent.keysym.mod & g_Config.m_InpIgnoredModifiers) { - return 0; + return KEY_UNKNOWN; } - int Scancode = g_Config.m_InpTranslatedKeys ? SDL_GetScancodeFromKey(KeyEvent.keysym.sym) : KeyEvent.keysym.scancode; + int Key = g_Config.m_InpTranslatedKeys ? SDL_GetScancodeFromKey(KeyEvent.keysym.sym) : KeyEvent.keysym.scancode; #if defined(CONF_PLATFORM_ANDROID) // Translate the Android back-button to the escape-key so it can be used to open/close the menu, close popups etc. - if(Scancode == KEY_AC_BACK) + if(Key == KEY_AC_BACK) { - Scancode = KEY_ESCAPE; + Key = KEY_ESCAPE; } #endif - return Scancode; + return Key; +} + +static int TranslateMouseButtonEventKey(const SDL_MouseButtonEvent &MouseButtonEvent) +{ + switch(MouseButtonEvent.button) + { + case SDL_BUTTON_LEFT: + return KEY_MOUSE_1; + case SDL_BUTTON_RIGHT: + return KEY_MOUSE_2; + case SDL_BUTTON_MIDDLE: + return KEY_MOUSE_3; + case SDL_BUTTON_X1: + return KEY_MOUSE_4; + case SDL_BUTTON_X2: + return KEY_MOUSE_5; + case 6: + return KEY_MOUSE_6; + case 7: + return KEY_MOUSE_7; + case 8: + return KEY_MOUSE_8; + case 9: + return KEY_MOUSE_9; + default: + return KEY_UNKNOWN; + } +} + +static int TranslateMouseWheelEventKey(const SDL_MouseWheelEvent &MouseWheelEvent) +{ + if(MouseWheelEvent.y > 0) + { + return KEY_MOUSE_WHEEL_UP; + } + else if(MouseWheelEvent.y < 0) + { + return KEY_MOUSE_WHEEL_DOWN; + } + else if(MouseWheelEvent.x > 0) + { + return KEY_MOUSE_WHEEL_RIGHT; + } + else if(MouseWheelEvent.x < 0) + { + return KEY_MOUSE_WHEEL_LEFT; + } + else + { + return KEY_UNKNOWN; + } } int CInput::Update() @@ -649,10 +700,16 @@ int CInput::Update() SDL_Event Event; bool IgnoreKeys = false; + + const auto &&AddKeyEventChecked = [&](int Key, int Flags) { + if(Key != KEY_UNKNOWN && !IgnoreKeys && !HasComposition()) + { + AddKeyEvent(Key, Flags); + } + }; + while(SDL_PollEvent(&Event)) { - int Scancode = 0; - int Action = IInput::FLAG_PRESS; switch(Event.type) { case SDL_SYSWMEVENT: @@ -678,11 +735,11 @@ int CInput::Update() // handle keys case SDL_KEYDOWN: - Scancode = TranslateScancode(Event.key); + AddKeyEventChecked(TranslateKeyEventKey(Event.key), IInput::FLAG_PRESS); break; + case SDL_KEYUP: - Action = IInput::FLAG_RELEASE; - Scancode = TranslateScancode(Event.key); + AddKeyEventChecked(TranslateKeyEventKey(Event.key), IInput::FLAG_RELEASE); break; // handle the joystick events @@ -708,41 +765,16 @@ int CInput::Update() break; // handle mouse buttons - case SDL_MOUSEBUTTONUP: - Action = IInput::FLAG_RELEASE; - - [[fallthrough]]; case SDL_MOUSEBUTTONDOWN: - if(Event.button.button == SDL_BUTTON_LEFT) - Scancode = KEY_MOUSE_1; - if(Event.button.button == SDL_BUTTON_RIGHT) - Scancode = KEY_MOUSE_2; - if(Event.button.button == SDL_BUTTON_MIDDLE) - Scancode = KEY_MOUSE_3; - if(Event.button.button == SDL_BUTTON_X1) - Scancode = KEY_MOUSE_4; - if(Event.button.button == SDL_BUTTON_X2) - Scancode = KEY_MOUSE_5; - if(Event.button.button == 6) - Scancode = KEY_MOUSE_6; - if(Event.button.button == 7) - Scancode = KEY_MOUSE_7; - if(Event.button.button == 8) - Scancode = KEY_MOUSE_8; - if(Event.button.button == 9) - Scancode = KEY_MOUSE_9; + AddKeyEventChecked(TranslateMouseButtonEventKey(Event.button), IInput::FLAG_PRESS); + break; + + case SDL_MOUSEBUTTONUP: + AddKeyEventChecked(TranslateMouseButtonEventKey(Event.button), IInput::FLAG_RELEASE); break; case SDL_MOUSEWHEEL: - if(Event.wheel.y > 0) - Scancode = KEY_MOUSE_WHEEL_UP; - if(Event.wheel.y < 0) - Scancode = KEY_MOUSE_WHEEL_DOWN; - if(Event.wheel.x > 0) - Scancode = KEY_MOUSE_WHEEL_LEFT; - if(Event.wheel.x < 0) - Scancode = KEY_MOUSE_WHEEL_RIGHT; - Action |= IInput::FLAG_RELEASE; + AddKeyEventChecked(TranslateMouseWheelEventKey(Event.wheel), IInput::FLAG_PRESS | IInput::FLAG_RELEASE); break; case SDL_FINGERDOWN: @@ -817,11 +849,6 @@ int CInput::Update() SDL_free(Event.drop.file); break; } - - if(Scancode > KEY_FIRST && Scancode < g_MaxKeys && !IgnoreKeys && !HasComposition()) - { - AddKeyEvent(Scancode, Action); - } } return 0; From 6ab23ed0723496f637e4f9164dd2486cc6e063f1 Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Thu, 19 Dec 2024 16:44:56 +0100 Subject: [PATCH 36/40] Remove use of magic strings `str_startswith_nocase` already returns a pointer to everything after the prefix. --- src/game/server/gamecontext.cpp | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 86ce39add39..6f78d4e038c 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -2176,29 +2176,22 @@ void CGameContext::OnSayNetMessage(const CNetMsg_Cl_Say *pMsg, int ClientId, con if(pMsg->m_pMessage[0] == '/') { - if(str_startswith_nocase(pMsg->m_pMessage + 1, "w ")) + const char *pWhisper; + if((pWhisper = str_startswith_nocase(pMsg->m_pMessage + 1, "w "))) { - char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + sizeof("w ")); - Whisper(pPlayer->GetCid(), aWhisperMsg); + Whisper(pPlayer->GetCid(), const_cast(pWhisper)); } - else if(str_startswith_nocase(pMsg->m_pMessage + 1, "whisper ")) + else if((pWhisper = str_startswith_nocase(pMsg->m_pMessage + 1, "whisper "))) { - char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + sizeof("whisper ")); - Whisper(pPlayer->GetCid(), aWhisperMsg); + Whisper(pPlayer->GetCid(), const_cast(pWhisper)); } - else if(str_startswith_nocase(pMsg->m_pMessage + 1, "c ")) + else if((pWhisper = str_startswith_nocase(pMsg->m_pMessage + 1, "c "))) { - char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + sizeof("c ")); - Converse(pPlayer->GetCid(), aWhisperMsg); + Converse(pPlayer->GetCid(), const_cast(pWhisper)); } - else if(str_startswith_nocase(pMsg->m_pMessage + 1, "converse ")) + else if((pWhisper = str_startswith_nocase(pMsg->m_pMessage + 1, "converse "))) { - char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + sizeof("converse ")); - Converse(pPlayer->GetCid(), aWhisperMsg); + Converse(pPlayer->GetCid(), const_cast(pWhisper)); } else { From f3af041415814341881a444556b66666e0d876db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 19 Dec 2024 17:31:12 +0100 Subject: [PATCH 37/40] Fix Android crash in release build due to ProGuard ProGuard optimizes out methods it considers unused in release build, but methods only called from native code appear unused. The ProGuard rule was broken from renaming the main activity and also did not consider the server service yet. --- scripts/android/files/proguard-rules.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/android/files/proguard-rules.pro b/scripts/android/files/proguard-rules.pro index 53c691af5c4..4f105e51fd0 100644 --- a/scripts/android/files/proguard-rules.pro +++ b/scripts/android/files/proguard-rules.pro @@ -16,7 +16,7 @@ # debugging stack traces. -keepattributes SourceFile,LineNumberTable --keepclassmembers, allowoptimization public class org.ddnet.client.NativeMain { +-keepclassmembers, allowoptimization public class org.ddnet.client.* { *; } From 85e246e843fbe4b66eab5764505f2a0c2782fe6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 20 Dec 2024 12:34:31 +0100 Subject: [PATCH 38/40] Add assertions to `CServer::GetClientAddr` functions Prevent use of uninitialized memory if the functions are used for a player that is not yet ingame. The check for the ingame state is also unnecessarily restrictive, as the client address should be available immediately. --- src/engine/server/server.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index a0e9aa927a7..c81ec1edc0b 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -621,8 +621,9 @@ void CServer::SetClientDDNetVersion(int ClientId, int DDNetVersion) void CServer::GetClientAddr(int ClientId, char *pAddrStr, int Size) const { - if(ClientId >= 0 && ClientId < MAX_CLIENTS && m_aClients[ClientId].m_State == CClient::STATE_INGAME) - net_addr_str(m_NetServer.ClientAddr(ClientId), pAddrStr, Size, false); + NETADDR Addr; + GetClientAddr(ClientId, &Addr); + net_addr_str(&Addr, pAddrStr, Size, false); } const char *CServer::ClientName(int ClientId) const @@ -3968,10 +3969,9 @@ CServer *CreateServer() { return new CServer(); } void CServer::GetClientAddr(int ClientId, NETADDR *pAddr) const { - if(ClientId >= 0 && ClientId < MAX_CLIENTS && m_aClients[ClientId].m_State == CClient::STATE_INGAME) - { - *pAddr = *m_NetServer.ClientAddr(ClientId); - } + dbg_assert(ClientId >= 0 && ClientId < MAX_CLIENTS, "ClientId is not valid"); + dbg_assert(m_aClients[ClientId].m_State != CServer::CClient::STATE_EMPTY, "Client slot is empty"); + *pAddr = *m_NetServer.ClientAddr(ClientId); } void CServer::ReadAnnouncementsFile() From b8af61d3f69c248d00c666d142b7c86faa46f43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 20 Dec 2024 12:25:26 +0100 Subject: [PATCH 39/40] Avoid string-based address comparison for `sv_vote_kick_min` Compare client addresses directly to determine the distinct client count excluding spectators. This is consistent with the `IServer::DistinctClientCount` function though this does not check for spectators. --- src/game/server/gamecontext.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 6f78d4e038c..57acc45a4b4 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -2326,12 +2326,12 @@ void CGameContext::OnCallVoteNetMessage(const CNetMsg_Cl_CallVote *pMsg, int Cli if(g_Config.m_SvVoteKickMin && !GetDDRaceTeam(ClientId)) { - char aaAddresses[MAX_CLIENTS][NETADDR_MAXSTRSIZE] = {{0}}; + NETADDR aAddresses[MAX_CLIENTS]; for(int i = 0; i < MAX_CLIENTS; i++) { if(m_apPlayers[i]) { - Server()->GetClientAddr(i, aaAddresses[i], NETADDR_MAXSTRSIZE); + Server()->GetClientAddr(i, &aAddresses[i]); } } int NumPlayers = 0; @@ -2344,7 +2344,7 @@ void CGameContext::OnCallVoteNetMessage(const CNetMsg_Cl_CallVote *pMsg, int Cli { if(m_apPlayers[j] && m_apPlayers[j]->GetTeam() != TEAM_SPECTATORS && !GetDDRaceTeam(j)) { - if(str_comp(aaAddresses[i], aaAddresses[j]) == 0) + if(!net_addr_comp_noport(&aAddresses[i], &aAddresses[j])) { NumPlayers--; break; From 1326deb7fef61728348694dffb07822d76920db7 Mon Sep 17 00:00:00 2001 From: BlaiZephyr Date: Fri, 20 Dec 2024 17:59:28 +0100 Subject: [PATCH 40/40] extend CONTRIBUTING.md and README.md --- CONTRIBUTING.md | 2 ++ README.md | 1 + 2 files changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4eefb46043f..c350e210805 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,8 @@ Such fix commits should ideally be squashed into one big commit using ``git comm A lot of the style offenses can be fixed automatically by running the fix script `./scripts/fix_style.py` +We use clang-format 10. If your package manager no longer provides this version, you can download it from https://pypi.org/project/clang-format/10.0.1.1/. + ### Upper camel case for variables, methods, class names With the exception of base/system.{h,cpp} diff --git a/README.md b/README.md index 779467b98b1..ab13f4c1b1a 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ You can get binary releases on the [DDNet website](https://ddnet.org/downloads/) - [Code Browser](https://ddnet.org/codebrowser/DDNet/) - [Source Code Documentation](https://codedoc.ddnet.org/) (very incomplete, only a few items are documented) +- [Contributing Guide](CONTRIBUTING.md) If you want to learn about the source code, you can check the [Development](https://wiki.ddnet.org/wiki/Development) article on the wiki.