mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
3879: Use SDL_FlashWindow to request user's attention r=heinrich5991 a=Jupeyy We should soon update to SDL 2.0.16 for our bundled libs, but don't merge before this happened. This drops the remaining X11 dependency. We should then also use GLEW with EGL for official builds for easy native wayland support(which i discussed before already) ## Checklist - [x] Tested the change ingame - [ ] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test if it works standalone, system.c especially - [ ] Considered possible null pointers and out of bounds array indexing - [ ] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) 4079: Make teehistorian easier to reproduce r=heinrich5991 a=Zwelf I've tested it ingame, but still have to make sure, that the ordering of the teehistorian messages are right. I would be really happy if we could land this before changing team joining logic (#4006 or /practice logic, but there isn't a pr for this yet), but I would like to make sure that the change is correct. ## Checklist - [x] Tested the change ingame - [ ] Tested in combination with possibly related configuration options - [x] Written a unit test if it works standalone, system.c especially - [x] Considered possible null pointers and out of bounds array indexing - [x] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: Jupeyy <jupjopjap@gmail.com> Co-authored-by: Zwelf <zwelf@strct.cc>
This commit is contained in:
commit
121c6d16f6
|
@ -599,9 +599,8 @@ elseif(TARGET_OS STREQUAL "android")
|
|||
else()
|
||||
find_package(Notify)
|
||||
find_package(OpenGL)
|
||||
find_package(X11)
|
||||
set(PLATFORM_CLIENT_LIBS ${OPENGL_gl_LIBRARY} ${X11_X11_LIB} ${NOTIFY_LIBRARIES})
|
||||
set(PLATFORM_CLIENT_INCLUDE_DIRS ${OPENGL_INCLUDE_DIR} ${X11_X11_INCLUDE_PATH} ${NOTIFY_INCLUDE_DIRS})
|
||||
set(PLATFORM_CLIENT_LIBS ${OPENGL_gl_LIBRARY} ${NOTIFY_LIBRARIES})
|
||||
set(PLATFORM_CLIENT_INCLUDE_DIRS ${OPENGL_INCLUDE_DIR} ${NOTIFY_INCLUDE_DIRS})
|
||||
set(PLATFORM_CLIENT)
|
||||
if(TARGET_OS STREQUAL "linux")
|
||||
set(PLATFORM_LIBS rt) # clock_gettime for glibc < 2.17
|
||||
|
|
|
@ -24,11 +24,6 @@
|
|||
#include "SDL_pixels.h"
|
||||
#include "SDL_video.h"
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_X11)
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#endif
|
||||
|
||||
#include <engine/shared/config.h>
|
||||
|
||||
#include <base/tl/threading.h>
|
||||
|
@ -1213,41 +1208,12 @@ void CGraphicsBackend_SDL_OpenGL::GetViewportSize(int &w, int &h)
|
|||
|
||||
void CGraphicsBackend_SDL_OpenGL::NotifyWindow()
|
||||
{
|
||||
// get window handle
|
||||
SDL_SysWMinfo info;
|
||||
SDL_VERSION(&info.version);
|
||||
if(!SDL_GetWindowWMInfo(m_pWindow, &info))
|
||||
#if SDL_MAJOR_VERSION > 2 || (SDL_MAJOR_VERSION == 2 && SDL_PATCHLEVEL >= 16)
|
||||
if(SDL_FlashWindow(m_pWindow, SDL_FlashOperation::SDL_FLASH_BRIEFLY) != 0)
|
||||
{
|
||||
dbg_msg("gfx", "unable to obtain window handle");
|
||||
// fails if SDL hasn't implemented it
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
FLASHWINFO desc;
|
||||
desc.cbSize = sizeof(desc);
|
||||
desc.hwnd = info.info.win.window;
|
||||
desc.dwFlags = FLASHW_TRAY;
|
||||
desc.uCount = 3; // flash 3 times
|
||||
desc.dwTimeout = 0;
|
||||
|
||||
FlashWindowEx(&desc);
|
||||
#elif defined(SDL_VIDEO_DRIVER_X11) && !defined(CONF_PLATFORM_MACOS)
|
||||
Display *pX11Dpy = info.info.x11.display;
|
||||
Window X11Win = info.info.x11.window;
|
||||
|
||||
static Atom s_DemandsAttention = XInternAtom(pX11Dpy, "_NET_WM_STATE_DEMANDS_ATTENTION", true);
|
||||
static Atom s_WmState = XInternAtom(pX11Dpy, "_NET_WM_STATE", true);
|
||||
|
||||
XEvent SndNtfyEvent = {ClientMessage};
|
||||
SndNtfyEvent.xclient.window = X11Win;
|
||||
SndNtfyEvent.xclient.message_type = s_WmState;
|
||||
SndNtfyEvent.xclient.format = 32;
|
||||
SndNtfyEvent.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
|
||||
SndNtfyEvent.xclient.data.l[1] = s_DemandsAttention;
|
||||
SndNtfyEvent.xclient.data.l[2] = 0;
|
||||
SndNtfyEvent.xclient.data.l[3] = 1; // normal application
|
||||
SndNtfyEvent.xclient.data.l[4] = 0;
|
||||
XSendEvent(pX11Dpy, XDefaultRootWindow(pX11Dpy), False, SubstructureNotifyMask | SubstructureRedirectMask, &SndNtfyEvent);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -301,6 +301,8 @@ public:
|
|||
|
||||
// DDRace
|
||||
|
||||
virtual void OnPreTickTeehistorian() = 0;
|
||||
|
||||
virtual void OnSetAuthed(int ClientID, int Level) = 0;
|
||||
virtual bool PlayerExists(int ClientID) const = 0;
|
||||
|
||||
|
|
|
@ -2596,6 +2596,8 @@ int CServer::Run()
|
|||
|
||||
while(t > TickStartTime(m_CurrentGameTick + 1))
|
||||
{
|
||||
GameServer()->OnPreTickTeehistorian();
|
||||
|
||||
for(int c = 0; c < MAX_CLIENTS; c++)
|
||||
if(m_aClients[c].m_State == CClient::STATE_INGAME)
|
||||
for(auto &Input : m_aClients[c].m_aInputs)
|
||||
|
|
|
@ -185,7 +185,7 @@ MACRO_CONFIG_STR(DbgStressServer, dbg_stress_server, 32, "localhost", CFGFLAG_CL
|
|||
// DDRace
|
||||
MACRO_CONFIG_STR(SvWelcome, sv_welcome, 64, "", CFGFLAG_SERVER, "Message that will be displayed to players who join the server")
|
||||
MACRO_CONFIG_INT(SvReservedSlots, sv_reserved_slots, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "The number of slots that are reserved for special players")
|
||||
MACRO_CONFIG_STR(SvReservedSlotsPass, sv_reserved_slots_pass, 32, "", CFGFLAG_SERVER, "The password that is required to use a reserved slot")
|
||||
MACRO_CONFIG_STR(SvReservedSlotsPass, sv_reserved_slots_pass, 32, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "The password that is required to use a reserved slot")
|
||||
MACRO_CONFIG_INT(SvHit, sv_hit, 1, 0, 1, CFGFLAG_SERVER | CFGFLAG_GAME, "Whether players can hammer/grenade/laser each other or not")
|
||||
MACRO_CONFIG_INT(SvEndlessDrag, sv_endless_drag, 0, 0, 1, CFGFLAG_SERVER | CFGFLAG_GAME, "Turns endless hooking on/off")
|
||||
MACRO_CONFIG_INT(SvTestingCommands, sv_test_cmds, 0, 0, 1, CFGFLAG_SERVER, "Turns testing commands aka cheats on/off (setting only works in initial config)")
|
||||
|
|
|
@ -12,3 +12,5 @@ UUID(TEEHISTORIAN_SAVE_SUCCESS, "teehistorian-save-success@ddnet.tw")
|
|||
UUID(TEEHISTORIAN_SAVE_FAILURE, "teehistorian-save-failure@ddnet.tw")
|
||||
UUID(TEEHISTORIAN_LOAD_SUCCESS, "teehistorian-load-success@ddnet.tw")
|
||||
UUID(TEEHISTORIAN_LOAD_FAILURE, "teehistorian-load-failure@ddnet.tw")
|
||||
UUID(TEEHISTORIAN_PLAYER_TEAM, "teehistorian-player-team@ddnet.tw")
|
||||
UUID(TEEHISTORIAN_TEAM_PRACTICE, "teehistorian-team-practice@ddnet.tw")
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
#include "SDL.h" // SDL_VIDEO_DRIVER_X11
|
||||
|
||||
#include <base/tl/string.h>
|
||||
|
||||
#include <base/math.h>
|
||||
|
|
|
@ -763,6 +763,22 @@ void CGameContext::SendTuningParams(int ClientID, int Zone)
|
|||
Server()->SendMsg(&Msg, MSGFLAG_VITAL, ClientID);
|
||||
}
|
||||
|
||||
void CGameContext::OnPreTickTeehistorian()
|
||||
{
|
||||
auto *pController = ((CGameControllerDDRace *)m_pController);
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(m_apPlayers[i] != nullptr)
|
||||
m_TeeHistorian.RecordPlayerTeam(i, pController->m_Teams.m_Core.Team(i));
|
||||
else
|
||||
m_TeeHistorian.RecordPlayerTeam(i, 0);
|
||||
}
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
m_TeeHistorian.RecordTeamPractice(i, pController->m_Teams.IsPractice(i));
|
||||
}
|
||||
}
|
||||
|
||||
void CGameContext::OnTick()
|
||||
{
|
||||
// check tuning
|
||||
|
@ -793,25 +809,6 @@ void CGameContext::OnTick()
|
|||
//if(world.paused) // make sure that the game object always updates
|
||||
m_pController->Tick();
|
||||
|
||||
if(m_TeeHistorianActive)
|
||||
{
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(m_apPlayers[i] && m_apPlayers[i]->GetCharacter())
|
||||
{
|
||||
CNetObj_CharacterCore Char;
|
||||
m_apPlayers[i]->GetCharacter()->GetCore().Write(&Char);
|
||||
m_TeeHistorian.RecordPlayer(i, &Char);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_TeeHistorian.RecordDeadPlayer(i);
|
||||
}
|
||||
}
|
||||
m_TeeHistorian.EndPlayers();
|
||||
m_TeeHistorian.BeginInputs();
|
||||
}
|
||||
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(m_apPlayers[i])
|
||||
|
@ -1072,6 +1069,27 @@ void CGameContext::OnTick()
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Record player position at the end of the tick
|
||||
if(m_TeeHistorianActive)
|
||||
{
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(m_apPlayers[i] && m_apPlayers[i]->GetCharacter())
|
||||
{
|
||||
CNetObj_CharacterCore Char;
|
||||
m_apPlayers[i]->GetCharacter()->GetCore().Write(&Char);
|
||||
m_TeeHistorian.RecordPlayer(i, &Char);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_TeeHistorian.RecordDeadPlayer(i);
|
||||
}
|
||||
}
|
||||
m_TeeHistorian.EndPlayers();
|
||||
m_TeeHistorian.BeginInputs();
|
||||
}
|
||||
// Warning: do not put code in this function directly above or below this comment
|
||||
}
|
||||
|
||||
// Server hooks
|
||||
|
|
|
@ -273,6 +273,7 @@ public:
|
|||
virtual const char *NetVersion() const;
|
||||
|
||||
// DDRace
|
||||
void OnPreTickTeehistorian();
|
||||
bool OnClientDDNetVersionKnown(int ClientID);
|
||||
virtual void FillAntibot(CAntibotRoundData *pData);
|
||||
int ProcessSpamProtection(int ClientID, bool RespectChatInitialDelay = true);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
static const char TEEHISTORIAN_NAME[] = "teehistorian@ddnet.tw";
|
||||
static const CUuid TEEHISTORIAN_UUID = CalculateUuid(TEEHISTORIAN_NAME);
|
||||
static const char TEEHISTORIAN_VERSION[] = "2";
|
||||
static const char TEEHISTORIAN_VERSION_MINOR[] = "2";
|
||||
|
||||
#define UUID(id, name) static const CUuid UUID_##id = CalculateUuid(name);
|
||||
#include <engine/shared/teehistorian_ex_chunks.h>
|
||||
|
@ -47,11 +48,17 @@ void CTeeHistorian::Reset(const CGameInfo *pGameInfo, WRITE_CALLBACK pfnWriteCal
|
|||
// Tick 0 is implicit at the start, game starts as tick 1.
|
||||
m_TickWritten = true;
|
||||
m_MaxClientID = MAX_CLIENTS;
|
||||
|
||||
// `m_PrevMaxClientID` is initialized in `BeginPlayers`
|
||||
for(auto &PrevPlayer : m_aPrevPlayers)
|
||||
{
|
||||
PrevPlayer.m_Alive = false;
|
||||
PrevPlayer.m_InputExists = false;
|
||||
PrevPlayer.m_Team = 0;
|
||||
}
|
||||
for(auto &PrevTeam : m_aPrevTeams)
|
||||
{
|
||||
PrevTeam.m_Practice = false;
|
||||
}
|
||||
m_pfnWriteCallback = pfnWriteCallback;
|
||||
m_pWriteCallbackUserdata = pUser;
|
||||
|
@ -86,9 +93,26 @@ void CTeeHistorian::WriteHeader(const CGameInfo *pGameInfo)
|
|||
|
||||
#define E(buf, str) EscapeJson(buf, sizeof(buf), str)
|
||||
|
||||
str_format(aJson, sizeof(aJson), "{\"comment\":\"%s\",\"version\":\"%s\",\"game_uuid\":\"%s\",\"server_version\":\"%s\",\"start_time\":\"%s\",\"server_name\":\"%s\",\"server_port\":\"%d\",\"game_type\":\"%s\",\"map_name\":\"%s\",\"map_size\":\"%d\",\"map_sha256\":\"%s\",\"map_crc\":\"%08x\",\"prng_description\":\"%s\",\"config\":{",
|
||||
str_format(aJson, sizeof(aJson),
|
||||
"{"
|
||||
"\"comment\":\"%s\","
|
||||
"\"version\":\"%s\","
|
||||
"\"version_minor\":\"%s\","
|
||||
"\"game_uuid\":\"%s\","
|
||||
"\"server_version\":\"%s\","
|
||||
"\"start_time\":\"%s\","
|
||||
"\"server_name\":\"%s\","
|
||||
"\"server_port\":\"%d\","
|
||||
"\"game_type\":\"%s\","
|
||||
"\"map_name\":\"%s\","
|
||||
"\"map_size\":\"%d\","
|
||||
"\"map_sha256\":\"%s\","
|
||||
"\"map_crc\":\"%08x\","
|
||||
"\"prng_description\":\"%s\","
|
||||
"\"config\":{",
|
||||
E(aCommentBuffer, TEEHISTORIAN_NAME),
|
||||
TEEHISTORIAN_VERSION,
|
||||
TEEHISTORIAN_VERSION_MINOR,
|
||||
aGameUuid,
|
||||
E(aServerVersionBuffer, pGameInfo->m_pServerVersion),
|
||||
E(aStartTimeBuffer, aStartTime),
|
||||
|
@ -293,6 +317,50 @@ void CTeeHistorian::RecordDeadPlayer(int ClientID)
|
|||
pPrev->m_Alive = false;
|
||||
}
|
||||
|
||||
void CTeeHistorian::RecordPlayerTeam(int ClientID, int Team)
|
||||
{
|
||||
if(m_aPrevPlayers[ClientID].m_Team != Team)
|
||||
{
|
||||
m_aPrevPlayers[ClientID].m_Team = Team;
|
||||
|
||||
EnsureTickWritten();
|
||||
|
||||
CPacker Buffer;
|
||||
Buffer.Reset();
|
||||
Buffer.AddInt(ClientID);
|
||||
Buffer.AddInt(Team);
|
||||
|
||||
if(m_Debug)
|
||||
{
|
||||
dbg_msg("teehistorian", "team_change cid=%d team=%d", ClientID, Team);
|
||||
}
|
||||
|
||||
WriteExtra(UUID_TEEHISTORIAN_PLAYER_TEAM, Buffer.Data(), Buffer.Size());
|
||||
}
|
||||
}
|
||||
|
||||
void CTeeHistorian::RecordTeamPractice(int Team, bool Practice)
|
||||
{
|
||||
if(m_aPrevTeams[Team].m_Practice != Practice)
|
||||
{
|
||||
m_aPrevTeams[Team].m_Practice = Practice;
|
||||
|
||||
EnsureTickWritten();
|
||||
|
||||
CPacker Buffer;
|
||||
Buffer.Reset();
|
||||
Buffer.AddInt(Team);
|
||||
Buffer.AddInt(Practice);
|
||||
|
||||
if(m_Debug)
|
||||
{
|
||||
dbg_msg("teehistorian", "team_rescue team=%d practice=%d", Team, Practice);
|
||||
}
|
||||
|
||||
WriteExtra(UUID_TEEHISTORIAN_TEAM_PRACTICE, Buffer.Data(), Buffer.Size());
|
||||
}
|
||||
}
|
||||
|
||||
void CTeeHistorian::Write(const void *pData, int DataSize)
|
||||
{
|
||||
m_pfnWriteCallback(pData, DataSize, m_pWriteCallbackUserdata);
|
||||
|
|
|
@ -57,6 +57,8 @@ public:
|
|||
void BeginPlayers();
|
||||
void RecordPlayer(int ClientID, const CNetObj_CharacterCore *pChar);
|
||||
void RecordDeadPlayer(int ClientID);
|
||||
void RecordPlayerTeam(int ClientID, int Team);
|
||||
void RecordTeamPractice(int Team, bool Practice);
|
||||
void EndPlayers();
|
||||
|
||||
void BeginInputs();
|
||||
|
@ -111,6 +113,14 @@ private:
|
|||
|
||||
CNetObj_PlayerInput m_Input;
|
||||
bool m_InputExists;
|
||||
|
||||
// DDNet team
|
||||
int m_Team;
|
||||
};
|
||||
|
||||
struct CTeam
|
||||
{
|
||||
bool m_Practice;
|
||||
};
|
||||
|
||||
WRITE_CALLBACK m_pfnWriteCallback;
|
||||
|
@ -124,6 +134,7 @@ private:
|
|||
int m_PrevMaxClientID;
|
||||
int m_MaxClientID;
|
||||
CPlayer m_aPrevPlayers[MAX_CLIENTS];
|
||||
CTeam m_aPrevTeams[MAX_CLIENTS];
|
||||
};
|
||||
|
||||
#endif // GAME_SERVER_TEEHISTORIAN_H
|
||||
|
|
|
@ -89,7 +89,7 @@ protected:
|
|||
void Expect(const unsigned char *pOutput, int OutputSize)
|
||||
{
|
||||
static CUuid TEEHISTORIAN_UUID = CalculateUuid("teehistorian@ddnet.tw");
|
||||
static const char PREFIX1[] = "{\"comment\":\"teehistorian@ddnet.tw\",\"version\":\"2\",\"game_uuid\":\"a1eb7182-796e-3b3e-941d-38ca71b2a4a8\",\"server_version\":\"DDNet test\",\"start_time\":\"";
|
||||
static const char PREFIX1[] = "{\"comment\":\"teehistorian@ddnet.tw\",\"version\":\"2\",\"version_minor\":\"2\",\"game_uuid\":\"a1eb7182-796e-3b3e-941d-38ca71b2a4a8\",\"server_version\":\"DDNet test\",\"start_time\":\"";
|
||||
static const char PREFIX2[] = "\",\"server_name\":\"server name\",\"server_port\":\"8303\",\"game_type\":\"game type\",\"map_name\":\"Kobra 3 Solo\",\"map_size\":\"903514\",\"map_sha256\":\"0123456789012345678901234567890123456789012345678901234567890123\",\"map_crc\":\"eceaf25c\",\"prng_description\":\"test-prng:02468ace\",\"config\":{},\"tuning\":{},\"uuids\":[";
|
||||
static const char PREFIX3[] = "]}";
|
||||
|
||||
|
@ -532,3 +532,43 @@ TEST_F(TeeHistorian, LoadFailed)
|
|||
Finish();
|
||||
Expect(EXPECTED, sizeof(EXPECTED));
|
||||
}
|
||||
|
||||
TEST_F(TeeHistorian, PlayerTeam)
|
||||
{
|
||||
const unsigned char EXPECTED[] = {
|
||||
// EX uuid=a111c04e-1ea8-38e0-90b1-d7f993ca0da9 datalen=2
|
||||
0x4a,
|
||||
0xa1, 0x11, 0xc0, 0x4e, 0x1e, 0xa8, 0x38, 0xe0,
|
||||
0x90, 0xb1, 0xd7, 0xf9, 0x93, 0xca, 0x0d, 0xa9,
|
||||
0x02,
|
||||
// player_id=33
|
||||
0x21,
|
||||
// team=54
|
||||
0x36,
|
||||
// FINISH
|
||||
0x40};
|
||||
|
||||
m_TH.RecordPlayerTeam(33, 54);
|
||||
Finish();
|
||||
Expect(EXPECTED, sizeof(EXPECTED));
|
||||
}
|
||||
|
||||
TEST_F(TeeHistorian, TeamPractice)
|
||||
{
|
||||
const unsigned char EXPECTED[] = {
|
||||
// EX uuid=5792834e-81d1-34c9-a29b-b5ff25dac3bc datalen=2
|
||||
0x4a,
|
||||
0x57, 0x92, 0x83, 0x4e, 0x81, 0xd1, 0x34, 0xc9,
|
||||
0xa2, 0x9b, 0xb5, 0xff, 0x25, 0xda, 0xc3, 0xbc,
|
||||
0x02,
|
||||
// team=23
|
||||
0x17,
|
||||
// practice=1
|
||||
0x01,
|
||||
// FINISH
|
||||
0x40};
|
||||
|
||||
m_TH.RecordTeamPractice(23, 1);
|
||||
Finish();
|
||||
Expect(EXPECTED, sizeof(EXPECTED));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue