Add a "connection problems" icon to nameplate

This commit is contained in:
furo 2024-10-26 18:55:46 +02:00
parent 5a716ae463
commit d0840e6458
14 changed files with 103 additions and 7 deletions

View file

@ -1486,6 +1486,7 @@ set(EXPECTED_DATA
languages/traditional_chinese.txt languages/traditional_chinese.txt
languages/turkish.txt languages/turkish.txt
languages/ukrainian.txt languages/ukrainian.txt
license.txt
mapres/basic_freeze.png mapres/basic_freeze.png
mapres/bg_cloud1.png mapres/bg_cloud1.png
mapres/bg_cloud2.png mapres/bg_cloud2.png
@ -1569,6 +1570,7 @@ set(EXPECTED_DATA
menuimages/local_server.png menuimages/local_server.png
menuimages/play_game.png menuimages/play_game.png
menuimages/settings.png menuimages/settings.png
network.png
particles.png particles.png
race_flag.png race_flag.png
shader/pipeline.frag shader/pipeline.frag

3
data/license.txt Normal file
View file

@ -0,0 +1,3 @@
network.png:
Copyright leovilok
CC BY-SA 3.0 license (https://creativecommons.org/licenses/by-sa/3.0/)

BIN
data/network.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -238,6 +238,7 @@ image_audio_source = Image("audio_source", "editor/audio_source.png")
image_strongweak = Image("strongweak", "strong_weak.png") image_strongweak = Image("strongweak", "strong_weak.png")
image_hud = Image("hud", "hud.png") image_hud = Image("hud", "hud.png")
image_extras = Image("extras", "extras.png") image_extras = Image("extras", "extras.png")
image_networkicons = Image("networkicons", "network.png", 1)
container.images.Add(image_null) container.images.Add(image_null)
container.images.Add(image_game) container.images.Add(image_game)
@ -256,6 +257,7 @@ container.images.Add(image_strongweak)
container.images.Add(image_hud) container.images.Add(image_hud)
container.images.Add(image_extras) container.images.Add(image_extras)
container.images.Add(Image("raceflag", "race_flag.png")) container.images.Add(Image("raceflag", "race_flag.png"))
container.images.Add(image_networkicons)
container.pickups.Add(Pickup("health")) container.pickups.Add(Pickup("health"))
container.pickups.Add(Pickup("armor")) container.pickups.Add(Pickup("armor"))
@ -277,6 +279,7 @@ set_audio_source = SpriteSet("audio_source", image_audio_source, 1, 1)
set_strongweak = SpriteSet("strongweak", image_strongweak, 2, 1) set_strongweak = SpriteSet("strongweak", image_strongweak, 2, 1)
set_hud = SpriteSet("hud", image_hud, 16, 16) set_hud = SpriteSet("hud", image_hud, 16, 16)
set_extras = SpriteSet("extras", image_extras, 16, 16) set_extras = SpriteSet("extras", image_extras, 16, 16)
set_networkicons = SpriteSet("networkicons", image_networkicons, 1, 2)
container.spritesets.Add(set_particles) container.spritesets.Add(set_particles)
container.spritesets.Add(set_game) container.spritesets.Add(set_game)
@ -289,6 +292,7 @@ container.spritesets.Add(set_audio_source)
container.spritesets.Add(set_strongweak) container.spritesets.Add(set_strongweak)
container.spritesets.Add(set_hud) container.spritesets.Add(set_hud)
container.spritesets.Add(set_extras) container.spritesets.Add(set_extras)
container.spritesets.Add(set_networkicons)
container.sprites.Add(Sprite("part_slice", set_particles, 0,0,1,1)) container.sprites.Add(Sprite("part_slice", set_particles, 0,0,1,1))
container.sprites.Add(Sprite("part_ball", set_particles, 1,0,1,1)) container.sprites.Add(Sprite("part_ball", set_particles, 1,0,1,1))
@ -454,6 +458,9 @@ container.sprites.Add(Sprite("hud_team0_mode", set_hud, 12,6,2,2))
container.sprites.Add(Sprite("part_snowflake", set_extras, 0,0,2,2)) container.sprites.Add(Sprite("part_snowflake", set_extras, 0,0,2,2))
container.sprites.Add(Sprite("part_sparkle", set_extras, 2,0,2,2)) container.sprites.Add(Sprite("part_sparkle", set_extras, 2,0,2,2))
container.sprites.Add(Sprite("network_good", set_networkicons, 0,0,1,1))
container.sprites.Add(Sprite("network_bad", set_networkicons, 0,1,1,1))
anim = Animation("base") anim = Animation("base")
anim.body.frames.Add(AnimKeyframe(0, 0, -4, 0)) anim.body.frames.Add(AnimKeyframe(0, 0, -4, 0))

View file

@ -28,7 +28,7 @@ GameInfoFlags2 = [
"ALLOW_X_SKINS", "GAMETYPE_CITY", "GAMETYPE_FDDRACE", "ENTITIES_FDDRACE", "HUD_HEALTH_ARMOR", "HUD_AMMO", "ALLOW_X_SKINS", "GAMETYPE_CITY", "GAMETYPE_FDDRACE", "ENTITIES_FDDRACE", "HUD_HEALTH_ARMOR", "HUD_AMMO",
"HUD_DDRACE", "NO_WEAK_HOOK", "NO_SKIN_CHANGE_FOR_FROZEN" "HUD_DDRACE", "NO_WEAK_HOOK", "NO_SKIN_CHANGE_FOR_FROZEN"
] ]
ExPlayerFlags = ["AFK", "PAUSED", "SPEC"] ExPlayerFlags = ["AFK", "PAUSED", "SPEC", "CONNECTION_PROBLEMS"]
LegacyProjectileFlags = [f"CLIENTID_BIT{i}" for i in range(8)] + [ LegacyProjectileFlags = [f"CLIENTID_BIT{i}" for i in range(8)] + [
"NO_OWNER", "IS_DDNET", "BOUNCE_HORIZONTAL", "BOUNCE_VERTICAL", "NO_OWNER", "IS_DDNET", "BOUNCE_HORIZONTAL", "BOUNCE_VERTICAL",
"EXPLOSIVE", "FREEZE", "EXPLOSIVE", "FREEZE",

View file

@ -44,6 +44,7 @@ public:
int m_DDNetVersion; int m_DDNetVersion;
const char *m_pDDNetVersionStr; const char *m_pDDNetVersionStr;
const CUuid *m_pConnectionId; const CUuid *m_pConnectionId;
int m_LastPacketTick;
}; };
int Tick() const { return m_CurrentGameTick; } int Tick() const { return m_CurrentGameTick; }

View file

@ -214,6 +214,7 @@ void CServer::CClient::Reset()
m_Snapshots.PurgeAll(); m_Snapshots.PurgeAll();
m_LastAckedSnapshot = -1; m_LastAckedSnapshot = -1;
m_LastInputTick = -1; m_LastInputTick = -1;
m_LastPacketTick = -1;
m_SnapRate = CClient::SNAPRATE_INIT; m_SnapRate = CClient::SNAPRATE_INIT;
m_Score = -1; m_Score = -1;
m_NextMapChunk = 0; m_NextMapChunk = 0;
@ -587,6 +588,7 @@ bool CServer::GetClientInfo(int ClientId, CClientInfo *pInfo) const
pInfo->m_Latency = m_aClients[ClientId].m_Latency; pInfo->m_Latency = m_aClients[ClientId].m_Latency;
pInfo->m_GotDDNetVersion = m_aClients[ClientId].m_DDNetVersionSettled; pInfo->m_GotDDNetVersion = m_aClients[ClientId].m_DDNetVersionSettled;
pInfo->m_DDNetVersion = m_aClients[ClientId].m_DDNetVersion >= 0 ? m_aClients[ClientId].m_DDNetVersion : VERSION_VANILLA; pInfo->m_DDNetVersion = m_aClients[ClientId].m_DDNetVersion >= 0 ? m_aClients[ClientId].m_DDNetVersion : VERSION_VANILLA;
pInfo->m_LastPacketTick = m_aClients[ClientId].m_LastPacketTick;
if(m_aClients[ClientId].m_GotDDNetVersionPacket) if(m_aClients[ClientId].m_GotDDNetVersionPacket)
{ {
pInfo->m_pConnectionId = &m_aClients[ClientId].m_ConnectionId; pInfo->m_pConnectionId = &m_aClients[ClientId].m_ConnectionId;
@ -1458,6 +1460,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
return; return;
} }
m_aClients[ClientId].m_LastPacketTick = Tick();
if(Config()->m_SvNetlimit && Msg != NETMSG_REQUEST_MAP_DATA) if(Config()->m_SvNetlimit && Msg != NETMSG_REQUEST_MAP_DATA)
{ {
int64_t Now = time_get(); int64_t Now = time_get();

View file

@ -145,6 +145,7 @@ public:
int m_LastAckedSnapshot; int m_LastAckedSnapshot;
int m_LastInputTick; int m_LastInputTick;
int m_LastPacketTick;
CSnapshotStorage m_Snapshots; CSnapshotStorage m_Snapshots;
CInput m_LatestInput; CInput m_LatestInput;

View file

@ -34,6 +34,7 @@ MACRO_CONFIG_INT(ClNameplatesIds, cl_nameplates_ids, 0, 0, 1, CFGFLAG_CLIENT | C
MACRO_CONFIG_INT(ClNameplatesOwn, cl_nameplates_own, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show own name plate (useful for demo recording)") MACRO_CONFIG_INT(ClNameplatesOwn, cl_nameplates_own, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show own name plate (useful for demo recording)")
MACRO_CONFIG_INT(ClNameplatesFriendMark, cl_nameplates_friendmark, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show friend mark (♥) in name plates") MACRO_CONFIG_INT(ClNameplatesFriendMark, cl_nameplates_friendmark, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show friend mark (♥) in name plates")
MACRO_CONFIG_INT(ClNameplatesStrong, cl_nameplates_strong, 0, 0, 2, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show strong/weak in name plates (0 - off, 1 - icons, 2 - icons + numbers)") MACRO_CONFIG_INT(ClNameplatesStrong, cl_nameplates_strong, 0, 0, 2, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show strong/weak in name plates (0 - off, 1 - icons, 2 - icons + numbers)")
MACRO_CONFIG_INT(ClNameplatesNetwork, cl_nameplates_network, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show a network icon if the player has connection problems")
MACRO_CONFIG_INT(ClTextEntities, cl_text_entities, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Render textual entity data") MACRO_CONFIG_INT(ClTextEntities, cl_text_entities, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Render textual entity data")
MACRO_CONFIG_INT(ClTextEntitiesSize, cl_text_entities_size, 100, 1, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Size of textual entity data from 1 to 100%") MACRO_CONFIG_INT(ClTextEntitiesSize, cl_text_entities_size, 100, 1, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Size of textual entity data from 1 to 100%")
MACRO_CONFIG_INT(ClStreamerMode, cl_streamer_mode, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Censor sensitive information such as /save password") MACRO_CONFIG_INT(ClStreamerMode, cl_streamer_mode, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Censor sensitive information such as /save password")

View file

@ -2774,6 +2774,12 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView)
g_Config.m_ClNameplatesStrong = g_Config.m_ClNameplatesStrong != 2 ? 2 : 1; g_Config.m_ClNameplatesStrong = g_Config.m_ClNameplatesStrong != 2 ? 2 : 1;
} }
LeftView.HSplitTop(LineSize, &Button, &LeftView);
if(DoButton_CheckBox(&g_Config.m_ClNameplatesNetwork, Localize("Show connection problems icon"), g_Config.m_ClNameplatesNetwork, &Button))
{
g_Config.m_ClNameplatesNetwork = g_Config.m_ClNameplatesNetwork ^ 1;
}
LeftView.HSplitTop(LineSize, &Button, &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)) if(DoButton_CheckBox(&g_Config.m_ClShowDirection, Localize("Show other players' key presses"), g_Config.m_ClShowDirection >= 1 && g_Config.m_ClShowDirection != 3, &Button))
{ {
@ -2863,6 +2869,8 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView)
TextRender()->Text(TeeRenderPos.x - TextRender()->TextWidth(FontSize, "0") / 2.0f, YOffset, FontSize, "0"); TextRender()->Text(TeeRenderPos.x - TextRender()->TextWidth(FontSize, "0") / 2.0f, YOffset, FontSize, "0");
} }
float XOffset = TeeRenderPos.x;
bool ChangedOffset = false;
if(g_Config.m_ClNameplatesStrong) if(g_Config.m_ClNameplatesStrong)
{ {
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_STRONGWEAK].m_Id); Graphics()->TextureSet(g_pData->m_aImages[IMAGE_STRONGWEAK].m_Id);
@ -2878,15 +2886,38 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView)
const float StrongImgSize = 40.0f; const float StrongImgSize = 40.0f;
YOffset -= StrongImgSize * ScaleY; YOffset -= StrongImgSize * ScaleY;
RenderTools()->DrawSprite(TeeRenderPos.x, YOffset + (StrongImgSize / 2.0f) * ScaleY, StrongImgSize); ChangedOffset = true;
if(g_Config.m_ClNameplatesNetwork)
XOffset = XOffset - (StrongImgSize / 2);
RenderTools()->DrawSprite(XOffset, YOffset + (StrongImgSize / 2.0f) * ScaleY, StrongImgSize);
Graphics()->QuadsEnd(); Graphics()->QuadsEnd();
if(g_Config.m_ClNameplatesStrong == 2) if(g_Config.m_ClNameplatesStrong == 2)
{ {
YOffset -= FontSize; TextRender()->Text(TeeRenderPos.x - TextRender()->TextWidth(FontSize, "0") / 2.0f, YOffset - FontSize, FontSize, "0");
TextRender()->Text(TeeRenderPos.x - TextRender()->TextWidth(FontSize, "0") / 2.0f, YOffset, FontSize, "0");
} }
} }
if(g_Config.m_ClNameplatesNetwork)
{
ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_NETWORKICONS].m_Id);
Graphics()->QuadsBegin();
RenderTools()->SelectSprite(SPRITE_NETWORK_BAD);
float ScaleX, ScaleY;
RenderTools()->GetSpriteScale(SPRITE_NETWORK_BAD, ScaleX, ScaleY);
const float NetworkImgSize = 40.0f;
if(!ChangedOffset)
YOffset -= NetworkImgSize * ScaleY;
else
XOffset += NetworkImgSize;
Graphics()->SetColor(Color);
RenderTools()->DrawSprite(XOffset, YOffset + (NetworkImgSize / 2.0f) * ScaleY, NetworkImgSize);
Graphics()->QuadsEnd();
}
} }
TextRender()->TextColor(TextRender()->DefaultTextColor()); TextRender()->TextColor(TextRender()->DefaultTextColor());

View file

@ -204,6 +204,9 @@ void CNamePlates::RenderNameplate(vec2 Position, const CNetObj_PlayerInfo *pPlay
} }
} }
float XOffset = Position.x;
bool ChangedOffset = false;
bool ConnectionProblems = m_pClient->m_aClients[pPlayerInfo->m_ClientId].m_ConnectionProblems;
if((g_Config.m_Debug || g_Config.m_ClNameplatesStrong) && g_Config.m_ClNameplates) if((g_Config.m_Debug || g_Config.m_ClNameplatesStrong) && g_Config.m_ClNameplates)
{ {
const bool Following = (m_pClient->m_Snap.m_SpecInfo.m_Active && !GameClient()->m_MultiViewActivated && m_pClient->m_Snap.m_SpecInfo.m_SpectatorId != SPEC_FREEVIEW); const bool Following = (m_pClient->m_Snap.m_SpecInfo.m_Active && !GameClient()->m_MultiViewActivated && m_pClient->m_Snap.m_SpecInfo.m_SpectatorId != SPEC_FREEVIEW);
@ -251,20 +254,53 @@ void CNamePlates::RenderNameplate(vec2 Position, const CNetObj_PlayerInfo *pPlay
const float StrongWeakImgSize = 40.0f; const float StrongWeakImgSize = 40.0f;
YOffset -= StrongWeakImgSize * ScaleY; YOffset -= StrongWeakImgSize * ScaleY;
RenderTools()->DrawSprite(Position.x, YOffset + (StrongWeakImgSize / 2.0f) * ScaleY, StrongWeakImgSize); ChangedOffset = true;
if(ConnectionProblems && g_Config.m_ClNameplatesNetwork)
XOffset = XOffset - (StrongWeakImgSize / 2);
RenderTools()->DrawSprite(XOffset, YOffset + (StrongWeakImgSize / 2.0f) * ScaleY, StrongWeakImgSize);
Graphics()->QuadsEnd(); Graphics()->QuadsEnd();
} }
if(g_Config.m_Debug || g_Config.m_ClNameplatesStrong == 2) if(g_Config.m_Debug || g_Config.m_ClNameplatesStrong == 2)
{ {
YOffset -= FontSize;
char aBuf[12]; char aBuf[12];
str_format(aBuf, sizeof(aBuf), "%d", Other.m_ExtendedData.m_StrongWeakId); str_format(aBuf, sizeof(aBuf), "%d", Other.m_ExtendedData.m_StrongWeakId);
TextRender()->Text(Position.x - TextRender()->TextWidth(FontSize, aBuf) / 2.0f, YOffset, FontSize, aBuf); TextRender()->Text(Position.x - TextRender()->TextWidth(FontSize, aBuf) / 2.0f, YOffset - FontSize, FontSize, aBuf);
} }
} }
} }
} }
// Connection problems icon
if(g_Config.m_ClNameplates && g_Config.m_ClNameplatesNetwork)
{
if(ConnectionProblems)
{
ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
if(OtherTeam && !ForceAlpha)
Color.a = g_Config.m_ClShowOthersAlpha / 100.0f;
else if(g_Config.m_ClNameplatesAlways == 0)
Color.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);
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_NETWORKICONS].m_Id);
Graphics()->QuadsBegin();
RenderTools()->SelectSprite(SPRITE_NETWORK_BAD);
float ScaleX, ScaleY;
RenderTools()->GetSpriteScale(SPRITE_NETWORK_BAD, ScaleX, ScaleY);
const float NetworkImgSize = 40.0f;
if(!ChangedOffset)
YOffset -= NetworkImgSize * ScaleY;
else
XOffset += NetworkImgSize;
Graphics()->SetColor(Color);
RenderTools()->DrawSprite(XOffset, YOffset + (NetworkImgSize / 2.0f) * ScaleY, NetworkImgSize);
Graphics()->QuadsEnd();
}
}
TextRender()->TextColor(TextRender()->DefaultTextColor()); TextRender()->TextColor(TextRender()->DefaultTextColor());
TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor()); TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor());

View file

@ -1615,6 +1615,7 @@ void CGameClient::OnNewSnapshot()
m_aClients[Item.m_Id].m_Afk = pInfo->m_Flags & EXPLAYERFLAG_AFK; m_aClients[Item.m_Id].m_Afk = pInfo->m_Flags & EXPLAYERFLAG_AFK;
m_aClients[Item.m_Id].m_Paused = pInfo->m_Flags & EXPLAYERFLAG_PAUSED; m_aClients[Item.m_Id].m_Paused = pInfo->m_Flags & EXPLAYERFLAG_PAUSED;
m_aClients[Item.m_Id].m_Spec = pInfo->m_Flags & EXPLAYERFLAG_SPEC; m_aClients[Item.m_Id].m_Spec = pInfo->m_Flags & EXPLAYERFLAG_SPEC;
m_aClients[Item.m_Id].m_ConnectionProblems = pInfo->m_Flags & EXPLAYERFLAG_CONNECTION_PROBLEMS;
if(Item.m_Id == m_Snap.m_LocalClientId && (m_aClients[Item.m_Id].m_Paused || m_aClients[Item.m_Id].m_Spec)) if(Item.m_Id == m_Snap.m_LocalClientId && (m_aClients[Item.m_Id].m_Paused || m_aClients[Item.m_Id].m_Spec))
{ {
@ -2498,6 +2499,7 @@ void CGameClient::CClientData::Reset()
m_Afk = false; m_Afk = false;
m_Paused = false; m_Paused = false;
m_Spec = false; m_Spec = false;
m_ConnectionProblems = false;
std::fill(std::begin(m_aSwitchStates), std::end(m_aSwitchStates), 0); std::fill(std::begin(m_aSwitchStates), std::end(m_aSwitchStates), 0);

View file

@ -420,6 +420,7 @@ public:
bool m_Afk; bool m_Afk;
bool m_Paused; bool m_Paused;
bool m_Spec; bool m_Spec;
bool m_ConnectionProblems;
// Editor allows 256 switches for now. // Editor allows 256 switches for now.
bool m_aSwitchStates[256]; bool m_aSwitchStates[256];

View file

@ -424,6 +424,14 @@ void CPlayer::Snap(int SnappingClient)
if(m_Paused == PAUSE_PAUSED) if(m_Paused == PAUSE_PAUSED)
pDDNetPlayer->m_Flags |= EXPLAYERFLAG_PAUSED; pDDNetPlayer->m_Flags |= EXPLAYERFLAG_PAUSED;
IServer::CClientInfo Info;
if(Server()->GetClientInfo(id, &Info))
{
// Send "connection problems" flag if player hasn't sent any packets after 5 seconds.
if(Server()->Tick() - Info.m_LastPacketTick >= 5 * Server()->TickSpeed())
pDDNetPlayer->m_Flags |= EXPLAYERFLAG_CONNECTION_PROBLEMS;
}
if(Server()->IsSixup(SnappingClient) && m_pCharacter && m_pCharacter->m_DDRaceState == DDRACE_STARTED && if(Server()->IsSixup(SnappingClient) && m_pCharacter && m_pCharacter->m_DDRaceState == DDRACE_STARTED &&
GameServer()->m_apPlayers[SnappingClient]->m_TimerType == TIMERTYPE_SIXUP) GameServer()->m_apPlayers[SnappingClient]->m_TimerType == TIMERTYPE_SIXUP)
{ {