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:
bors[bot] 2022-08-27 12:40:09 +00:00 committed by GitHub
commit 8ac260a3a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 94 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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();
}
}