mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Merge #5777
5777: Add period/comma hotkeys to skip one tick forward/backward, various other improvements to demo skipping r=Jupeyy a=Robyt3 Mostly from upstream https://github.com/teeworlds/teeworlds/pull/2904, with some more fixes and refactorings. ## 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 (especially base/) or added coverage to integration test - [ ] 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: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
commit
8ac260a3a9
|
@ -246,10 +246,6 @@ void CDemoRecorder::WriteTickMarker(int Tick, int Keyframe)
|
|||
|
||||
void CDemoRecorder::Write(int Type, const void *pData, int Size)
|
||||
{
|
||||
char aBuffer[64 * 1024];
|
||||
char aBuffer2[64 * 1024];
|
||||
unsigned char aChunk[3];
|
||||
|
||||
if(!m_File)
|
||||
return;
|
||||
|
||||
|
@ -258,6 +254,8 @@ void CDemoRecorder::Write(int Type, const void *pData, int Size)
|
|||
|
||||
/* pad the data with 0 so we get an alignment of 4,
|
||||
else the compression won't work and miss some bytes */
|
||||
char aBuffer[64 * 1024];
|
||||
char aBuffer2[64 * 1024];
|
||||
mem_copy(aBuffer2, pData, Size);
|
||||
while(Size & 3)
|
||||
aBuffer2[Size++] = 0;
|
||||
|
@ -269,6 +267,7 @@ void CDemoRecorder::Write(int Type, const void *pData, int Size)
|
|||
if(Size < 0)
|
||||
return;
|
||||
|
||||
unsigned char aChunk[3];
|
||||
aChunk[0] = ((Type & 0x3) << 5);
|
||||
if(Size < 30)
|
||||
{
|
||||
|
@ -418,14 +417,13 @@ void CDemoPlayer::SetListener(IListener *pListener)
|
|||
|
||||
int CDemoPlayer::ReadChunkHeader(int *pType, int *pSize, int *pTick)
|
||||
{
|
||||
unsigned char Chunk = 0;
|
||||
|
||||
*pSize = 0;
|
||||
*pType = 0;
|
||||
|
||||
if(m_File == NULL)
|
||||
return -1;
|
||||
|
||||
unsigned char Chunk = 0;
|
||||
if(io_read(m_File, &Chunk, sizeof(Chunk)) != sizeof(Chunk))
|
||||
return -1;
|
||||
|
||||
|
@ -479,21 +477,19 @@ int CDemoPlayer::ReadChunkHeader(int *pType, int *pSize, int *pTick)
|
|||
|
||||
void CDemoPlayer::ScanFile()
|
||||
{
|
||||
long StartPos;
|
||||
CHeap Heap;
|
||||
CKeyFrameSearch *pFirstKey = 0;
|
||||
CKeyFrameSearch *pCurrentKey = 0;
|
||||
//DEMOREC_CHUNK chunk;
|
||||
int ChunkSize, ChunkType, ChunkTick = 0;
|
||||
int i;
|
||||
int ChunkTick = 0;
|
||||
|
||||
StartPos = io_tell(m_File);
|
||||
long StartPos = io_tell(m_File);
|
||||
m_Info.m_SeekablePoints = 0;
|
||||
|
||||
while(true)
|
||||
{
|
||||
long CurrentPos = io_tell(m_File);
|
||||
|
||||
int ChunkSize, ChunkType;
|
||||
if(ReadChunkHeader(&ChunkType, &ChunkSize, &ChunkTick))
|
||||
break;
|
||||
|
||||
|
@ -526,6 +522,7 @@ void CDemoPlayer::ScanFile()
|
|||
}
|
||||
|
||||
// copy all the frames to an array instead for fast access
|
||||
int i;
|
||||
m_pKeyFrames = (CKeyFrame *)calloc(maximum(m_Info.m_SeekablePoints, 1), sizeof(CKeyFrame));
|
||||
for(pCurrentKey = pFirstKey, i = 0; pCurrentKey; pCurrentKey = pCurrentKey->m_pNext, i++)
|
||||
m_pKeyFrames[i] = pCurrentKey->m_Frame;
|
||||
|
@ -885,13 +882,7 @@ bool CDemoPlayer::ExtractMap(class IStorage *pStorage)
|
|||
return true;
|
||||
}
|
||||
|
||||
int CDemoPlayer::NextFrame()
|
||||
{
|
||||
DoTick();
|
||||
return IsPlaying();
|
||||
}
|
||||
|
||||
int64_t CDemoPlayer::time()
|
||||
int64_t CDemoPlayer::Time()
|
||||
{
|
||||
#if defined(CONF_VIDEORECORDER)
|
||||
static bool s_Recording = false;
|
||||
|
@ -927,7 +918,7 @@ int CDemoPlayer::Play()
|
|||
|
||||
// set start info
|
||||
m_Info.m_CurrentTime = m_Info.m_PreviousTick * time_freq() / SERVER_TICK_SPEED;
|
||||
m_Info.m_LastUpdate = time();
|
||||
m_Info.m_LastUpdate = Time();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -948,19 +939,16 @@ int CDemoPlayer::SetPos(int WantedTick)
|
|||
if(!m_File)
|
||||
return -1;
|
||||
|
||||
// -5 because we have to have a current tick and previous tick when we do the playback
|
||||
WantedTick = clamp(WantedTick, m_Info.m_Info.m_FirstTick, m_Info.m_Info.m_LastTick) - 5;
|
||||
WantedTick = clamp(WantedTick, m_Info.m_Info.m_FirstTick, m_Info.m_Info.m_LastTick);
|
||||
const int KeyFrameWantedTick = WantedTick - 5; // -5 because we have to have a current tick and previous tick when we do the playback
|
||||
const float Percent = (KeyFrameWantedTick - m_Info.m_Info.m_FirstTick) / (float)(m_Info.m_Info.m_LastTick - m_Info.m_Info.m_FirstTick);
|
||||
|
||||
// get correct key frame
|
||||
int KeyFrame = 0;
|
||||
while(KeyFrame < m_Info.m_SeekablePoints - 1 && m_pKeyFrames[KeyFrame].m_Tick < WantedTick)
|
||||
{
|
||||
int KeyFrame = clamp((int)(m_Info.m_SeekablePoints * Percent), 0, m_Info.m_SeekablePoints - 1);
|
||||
while(KeyFrame < m_Info.m_SeekablePoints - 1 && m_pKeyFrames[KeyFrame].m_Tick < KeyFrameWantedTick)
|
||||
KeyFrame++;
|
||||
}
|
||||
while(KeyFrame > 0 && m_pKeyFrames[KeyFrame].m_Tick > WantedTick)
|
||||
{
|
||||
while(KeyFrame > 0 && m_pKeyFrames[KeyFrame].m_Tick > KeyFrameWantedTick)
|
||||
KeyFrame--;
|
||||
}
|
||||
|
||||
// seek to the correct key frame
|
||||
io_seek(m_File, m_pKeyFrames[KeyFrame].m_Filepos, IOSEEK_START);
|
||||
|
@ -970,7 +958,7 @@ int CDemoPlayer::SetPos(int WantedTick)
|
|||
m_Info.m_PreviousTick = -1;
|
||||
|
||||
// playback everything until we hit our tick
|
||||
while(m_Info.m_PreviousTick < WantedTick && IsPlaying())
|
||||
while(m_Info.m_NextTick < WantedTick)
|
||||
DoTick();
|
||||
|
||||
Play();
|
||||
|
@ -991,22 +979,19 @@ void CDemoPlayer::SetSpeedIndex(int Offset)
|
|||
|
||||
int CDemoPlayer::Update(bool RealTime)
|
||||
{
|
||||
int64_t Now = time();
|
||||
int64_t Now = Time();
|
||||
int64_t Deltatime = Now - m_Info.m_LastUpdate;
|
||||
m_Info.m_LastUpdate = Now;
|
||||
|
||||
if(!IsPlaying())
|
||||
return 0;
|
||||
|
||||
if(m_Info.m_Info.m_Paused)
|
||||
const int64_t Freq = time_freq();
|
||||
if(!m_Info.m_Info.m_Paused)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
int64_t Freq = time_freq();
|
||||
m_Info.m_CurrentTime += (int64_t)(Deltatime * (double)m_Info.m_Info.m_Speed);
|
||||
|
||||
while(true)
|
||||
while(!m_Info.m_Info.m_Paused)
|
||||
{
|
||||
int64_t CurtickStart = (m_Info.m_Info.m_CurrentTick) * Freq / SERVER_TICK_SPEED;
|
||||
|
||||
|
@ -1016,34 +1001,31 @@ int CDemoPlayer::Update(bool RealTime)
|
|||
|
||||
// do one more tick
|
||||
DoTick();
|
||||
|
||||
if(m_Info.m_Info.m_Paused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// update intratick
|
||||
{
|
||||
int64_t CurtickStart = (m_Info.m_Info.m_CurrentTick) * Freq / SERVER_TICK_SPEED;
|
||||
int64_t PrevtickStart = (m_Info.m_PreviousTick) * Freq / SERVER_TICK_SPEED;
|
||||
m_Info.m_IntraTick = (m_Info.m_CurrentTime - PrevtickStart) / (float)(CurtickStart - PrevtickStart);
|
||||
m_Info.m_IntraTickSincePrev = (m_Info.m_CurrentTime - PrevtickStart) / (float)(Freq / SERVER_TICK_SPEED);
|
||||
m_Info.m_TickTime = (m_Info.m_CurrentTime - PrevtickStart) / (float)Freq;
|
||||
if(m_UpdateIntraTimesFunc)
|
||||
m_UpdateIntraTimesFunc();
|
||||
}
|
||||
|
||||
if(m_Info.m_Info.m_CurrentTick == m_Info.m_PreviousTick ||
|
||||
m_Info.m_Info.m_CurrentTick == m_Info.m_NextTick)
|
||||
{
|
||||
if(m_pConsole)
|
||||
{
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "tick error prev=%d cur=%d next=%d",
|
||||
m_Info.m_PreviousTick, m_Info.m_Info.m_CurrentTick, m_Info.m_NextTick);
|
||||
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "demo_player", aBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update intratick
|
||||
{
|
||||
int64_t CurtickStart = (m_Info.m_Info.m_CurrentTick) * Freq / SERVER_TICK_SPEED;
|
||||
int64_t PrevtickStart = (m_Info.m_PreviousTick) * Freq / SERVER_TICK_SPEED;
|
||||
m_Info.m_IntraTick = (m_Info.m_CurrentTime - PrevtickStart) / (float)(CurtickStart - PrevtickStart);
|
||||
m_Info.m_IntraTickSincePrev = (m_Info.m_CurrentTime - PrevtickStart) / (float)(Freq / SERVER_TICK_SPEED);
|
||||
m_Info.m_TickTime = (m_Info.m_CurrentTime - PrevtickStart) / (float)Freq;
|
||||
if(m_UpdateIntraTimesFunc)
|
||||
m_UpdateIntraTimesFunc();
|
||||
}
|
||||
|
||||
if(m_Info.m_Info.m_CurrentTick == m_Info.m_PreviousTick || m_Info.m_Info.m_CurrentTick == m_Info.m_NextTick)
|
||||
{
|
||||
if(m_pConsole)
|
||||
{
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "tick error prev=%d cur=%d next=%d",
|
||||
m_Info.m_PreviousTick, m_Info.m_Info.m_CurrentTick, m_Info.m_NextTick);
|
||||
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "demo_player", aBuf);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -117,9 +117,8 @@ private:
|
|||
int ReadChunkHeader(int *pType, int *pSize, int *pTick);
|
||||
void DoTick();
|
||||
void ScanFile();
|
||||
int NextFrame();
|
||||
|
||||
int64_t time();
|
||||
int64_t Time();
|
||||
|
||||
public:
|
||||
CDemoPlayer(class CSnapshotDelta *pSnapshotDelta);
|
||||
|
|
|
@ -87,7 +87,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
|
|||
const float ButtonbarHeight = 20.0f;
|
||||
const float NameBarHeight = 20.0f;
|
||||
const float Margins = 5.0f;
|
||||
static int64_t LastSpeedChange = 0;
|
||||
static int64_t s_LastSpeedChange = 0;
|
||||
|
||||
// render popups
|
||||
if(m_DemoPlayerState == DEMOPLAYER_SLICE_SAVE)
|
||||
|
@ -179,6 +179,8 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
|
|||
}
|
||||
|
||||
// handle keyboard shortcuts independent of active menu
|
||||
float PositionToSeek = -1.0f;
|
||||
float TimeToSeek = 0.0f;
|
||||
if(m_pClient->m_GameConsole.IsClosed() && m_DemoPlayerState == DEMOPLAYER_NONE && g_Config.m_ClDemoKeyboardShortcuts)
|
||||
{
|
||||
// increase/decrease speed
|
||||
|
@ -187,12 +189,12 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
|
|||
if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP) || Input()->KeyPress(KEY_UP))
|
||||
{
|
||||
DemoPlayer()->SetSpeedIndex(+1);
|
||||
LastSpeedChange = time_get();
|
||||
s_LastSpeedChange = time_get();
|
||||
}
|
||||
else if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN) || Input()->KeyPress(KEY_DOWN))
|
||||
{
|
||||
DemoPlayer()->SetSpeedIndex(-1);
|
||||
LastSpeedChange = time_get();
|
||||
s_LastSpeedChange = time_get();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,19 +214,19 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
|
|||
// seek backward/forward 10/5 seconds
|
||||
if(Input()->KeyPress(KEY_J))
|
||||
{
|
||||
DemoPlayer()->SeekTime(-10.0f);
|
||||
TimeToSeek = -10.0f;
|
||||
}
|
||||
else if(Input()->KeyPress(KEY_L))
|
||||
{
|
||||
DemoPlayer()->SeekTime(10.0f);
|
||||
TimeToSeek = 10.0f;
|
||||
}
|
||||
else if(Input()->KeyPress(KEY_LEFT))
|
||||
{
|
||||
DemoPlayer()->SeekTime(-5.0f);
|
||||
TimeToSeek = -5.0f;
|
||||
}
|
||||
else if(Input()->KeyPress(KEY_RIGHT))
|
||||
{
|
||||
DemoPlayer()->SeekTime(5.0f);
|
||||
TimeToSeek = 5.0f;
|
||||
}
|
||||
|
||||
// seek to 0-90%
|
||||
|
@ -233,7 +235,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
|
|||
{
|
||||
if(Input()->KeyPress(aSeekPercentKeys[i]))
|
||||
{
|
||||
DemoPlayer()->SeekPercent(i * 0.1f);
|
||||
PositionToSeek = i * 0.1f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -241,18 +243,31 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
|
|||
// seek to the beginning/end
|
||||
if(Input()->KeyPress(KEY_HOME))
|
||||
{
|
||||
DemoPlayer()->SeekPercent(0.0f);
|
||||
PositionToSeek = 0.0f;
|
||||
}
|
||||
else if(Input()->KeyPress(KEY_END))
|
||||
{
|
||||
DemoPlayer()->SeekPercent(1.0f);
|
||||
PositionToSeek = 1.0f;
|
||||
}
|
||||
|
||||
// Advance single frame forward/backward with period/comma key
|
||||
const bool TickForwards = Input()->KeyPress(KEY_PERIOD);
|
||||
const bool TickBackwards = Input()->KeyPress(KEY_COMMA);
|
||||
if(TickForwards || TickBackwards)
|
||||
{
|
||||
m_pClient->m_SuppressEvents = true;
|
||||
DemoPlayer()->SetPos(pInfo->m_CurrentTick + (TickForwards ? 3 : 0));
|
||||
m_pClient->m_SuppressEvents = false;
|
||||
DemoPlayer()->Pause();
|
||||
m_pClient->m_MapLayersBackGround.EnvelopeUpdate();
|
||||
m_pClient->m_MapLayersForeGround.EnvelopeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
float TotalHeight = SeekBarHeight + ButtonbarHeight + NameBarHeight + Margins * 3;
|
||||
|
||||
// render speed info
|
||||
if(g_Config.m_ClDemoShowSpeed && time_get() - LastSpeedChange < time_freq() * 1)
|
||||
if(g_Config.m_ClDemoShowSpeed && time_get() - s_LastSpeedChange < time_freq() * 1)
|
||||
{
|
||||
CUIRect Screen = *UI()->Screen();
|
||||
|
||||
|
@ -351,32 +366,23 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
|
|||
UI()->SetActiveItem(nullptr);
|
||||
else
|
||||
{
|
||||
static float PrevAmount = 0.0f;
|
||||
static float s_PrevAmount = 0.0f;
|
||||
float AmountSeek = (UI()->MouseX() - SeekBar.x) / SeekBar.w;
|
||||
|
||||
if(Input()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT))
|
||||
{
|
||||
AmountSeek = PrevAmount + (AmountSeek - PrevAmount) * 0.05f;
|
||||
|
||||
if(AmountSeek > 0.0f && AmountSeek < 1.0f && absolute(PrevAmount - AmountSeek) >= 0.0001f)
|
||||
AmountSeek = s_PrevAmount + (AmountSeek - s_PrevAmount) * 0.05f;
|
||||
if(AmountSeek > 0.0f && AmountSeek < 1.0f && absolute(s_PrevAmount - AmountSeek) >= 0.0001f)
|
||||
{
|
||||
m_pClient->m_SuppressEvents = true;
|
||||
DemoPlayer()->SeekPercent(AmountSeek);
|
||||
m_pClient->m_SuppressEvents = false;
|
||||
m_pClient->m_MapLayersBackGround.EnvelopeUpdate();
|
||||
m_pClient->m_MapLayersForeGround.EnvelopeUpdate();
|
||||
PositionToSeek = AmountSeek;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(AmountSeek > 0.0f && AmountSeek < 1.0f && absolute(PrevAmount - AmountSeek) >= 0.001f)
|
||||
if(AmountSeek > 0.0f && AmountSeek < 1.0f && absolute(s_PrevAmount - AmountSeek) >= 0.001f)
|
||||
{
|
||||
PrevAmount = AmountSeek;
|
||||
m_pClient->m_SuppressEvents = true;
|
||||
DemoPlayer()->SeekPercent(AmountSeek);
|
||||
m_pClient->m_SuppressEvents = false;
|
||||
m_pClient->m_MapLayersBackGround.EnvelopeUpdate();
|
||||
m_pClient->m_MapLayersForeGround.EnvelopeUpdate();
|
||||
s_PrevAmount = AmountSeek;
|
||||
PositionToSeek = AmountSeek;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -394,7 +400,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
|
|||
if(CurrentTick == TotalTicks)
|
||||
{
|
||||
DemoPlayer()->Pause();
|
||||
DemoPlayer()->SeekPercent(0.0f);
|
||||
PositionToSeek = 0.0f;
|
||||
}
|
||||
|
||||
bool IncreaseDemoSpeed = false, DecreaseDemoSpeed = false;
|
||||
|
@ -512,12 +518,24 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
|
|||
if(IncreaseDemoSpeed)
|
||||
{
|
||||
DemoPlayer()->SetSpeedIndex(+1);
|
||||
LastSpeedChange = time_get();
|
||||
s_LastSpeedChange = time_get();
|
||||
}
|
||||
else if(DecreaseDemoSpeed)
|
||||
{
|
||||
DemoPlayer()->SetSpeedIndex(-1);
|
||||
LastSpeedChange = time_get();
|
||||
s_LastSpeedChange = time_get();
|
||||
}
|
||||
|
||||
if((PositionToSeek >= 0.0f && PositionToSeek <= 1.0f) || TimeToSeek != 0.0f)
|
||||
{
|
||||
m_pClient->m_SuppressEvents = true;
|
||||
if(TimeToSeek != 0.0f)
|
||||
DemoPlayer()->SeekTime(TimeToSeek);
|
||||
else
|
||||
DemoPlayer()->SeekPercent(PositionToSeek);
|
||||
m_pClient->m_SuppressEvents = false;
|
||||
m_pClient->m_MapLayersBackGround.EnvelopeUpdate();
|
||||
m_pClient->m_MapLayersForeGround.EnvelopeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue