Browse Source

Stream demo messages

This entirely eliminates the demo memory overhead.
pull/6554/head
Gleb Mazovetskiy 3 years ago
parent
commit
70ff515f48
  1. 2
      Packaging/OpenDingux/profile-generate.sh
  2. 55
      Packaging/OpenDingux/rg99-pgo.md
  3. 304
      Source/engine/demomode.cpp
  4. 11
      Source/pfile.cpp

2
Packaging/OpenDingux/profile-generate.sh

@ -6,5 +6,7 @@ SAVE_DIR="$(mktemp -d)"
ln -s "${PWD}/demo_0_reference_spawn_0_sv" "${SAVE_DIR}/" ln -s "${PWD}/demo_0_reference_spawn_0_sv" "${SAVE_DIR}/"
ln -s "${PWD}/demo_0.dmo" "${SAVE_DIR}/" ln -s "${PWD}/demo_0.dmo" "${SAVE_DIR}/"
cp -r "${PWD}/spawn_0_sv" "${SAVE_DIR}/" cp -r "${PWD}/spawn_0_sv" "${SAVE_DIR}/"
rm -rf "${HOME}/devilutionx-profile"
mkdir -p "${HOME}/devilutionx-profile"
./devilutionx --diablo --spawn --demo 0 --timedemo --save-dir "$SAVE_DIR" --data-dir ~/.local/share/diasurgical/devilution ./devilutionx --diablo --spawn --demo 0 --timedemo --save-dir "$SAVE_DIR" --data-dir ~/.local/share/diasurgical/devilution
rm -rf "$SAVE_DIR" rm -rf "$SAVE_DIR"

55
Packaging/OpenDingux/rg99-pgo.md

@ -35,3 +35,58 @@ Here are the instructions for producing a PGO'd build.
``` ```
7. The final package is at `build-rg99/devilutionx-rg99.opk`. 7. The final package is at `build-rg99/devilutionx-rg99.opk`.
## Remote Debugging with VS Code
If the demo crashes and you cannot reproduce this on PC, you can
use a remote debugger to diagnose the issue.
Unpack the package and copy it to the RG99:
```bash
cd build-rg99
rm -rf squashfs-root
unsquashfs devilutionx-rg99.opk
ssh rg99 'rm -rf /media/data/local/home/squashfs-root'
scp -r -O squashfs-root/ rg99:/media/data/local/home/squashfs-root
```
Then, on RG99, prepare the demo files and run `gdbserver`:
```bash
mkdir -p demo
cp -r squashfs-root/demo_0* demo
cp -r squashfs-root/spawn_0_sv demo
cd squashfs-root
gdbserver 10.1.1.1:8001 devilutionx --diablo --spawn --demo 0 --timedemo \
--save-dir ~/demo --data-dir ~/.local/share/diasurgical/devilution
```
Then, on the PC, add the following VS Code configuration to `.vscode/launch.json`:
```json
{
"name": "rg99 remote debug",
"type": "cppdbg",
"request": "launch",
"program": "build-rg99/devilutionx",
"stopAtEntry": true,
"miDebuggerPath": "/opt/rs90-toolchain/bin/mipsel-linux-gdb",
"miDebuggerArgs": "-ix /opt/rs90-toolchain/mipsel-rs90-linux-musl/sysroot/usr/share/buildroot/gdbinit",
"MIMode": "gdb",
"miDebuggerServerAddress": "10.1.1.3:8001",
"targetArchitecture": "mips",
"additionalSOLibSearchPath": "/opt/rs90-toolchain/mipsel-rs90-linux-musl/sysroot",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"externalConsole": false,
"cwd": "${workspaceFolder}"
}
```
Finally, run the configuration from the "Run and Debug" VS Code tab.

304
Source/engine/demomode.cpp

@ -2,7 +2,6 @@
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <deque>
#include <limits> #include <limits>
#include <optional> #include <optional>
@ -65,13 +64,6 @@ struct KeyEventData {
uint16_t mod; uint16_t mod;
}; };
union DemoMsgEventData {
MouseMotionEventData motion;
MouseButtonEventData button;
MouseWheelEventData wheel;
KeyEventData key;
};
struct DemoMsg { struct DemoMsg {
enum EventType : uint8_t { enum EventType : uint8_t {
GameTick = 0, GameTick = 0,
@ -93,6 +85,12 @@ struct DemoMsg {
EventType type; EventType type;
uint8_t progressToNextGameTick; uint8_t progressToNextGameTick;
union {
MouseMotionEventData motion;
MouseButtonEventData button;
MouseWheelEventData wheel;
KeyEventData key;
};
[[nodiscard]] bool isEvent() const [[nodiscard]] bool isEvent() const
{ {
@ -100,7 +98,11 @@ struct DemoMsg {
} }
}; };
FILE *DemoFile;
int DemoFileVersion;
int DemoNumber = -1; int DemoNumber = -1;
std::optional<DemoMsg> CurrentDemoMessage;
bool Timedemo = false; bool Timedemo = false;
int RecordNumber = -1; int RecordNumber = -1;
bool CreateDemoReference = false; bool CreateDemoReference = false;
@ -142,46 +144,6 @@ uint32_t StartTime = 0;
uint16_t DemoGraphicsWidth = 640; uint16_t DemoGraphicsWidth = 640;
uint16_t DemoGraphicsHeight = 480; uint16_t DemoGraphicsHeight = 480;
std::deque<DemoMsg> DemoMessageQueue;
std::deque<MouseMotionEventData> MouseMotionEventDataQueue;
std::deque<MouseButtonEventData> MouseButtonEventDataQueue;
std::deque<MouseWheelEventData> MouseWheelEventDataQueue;
std::deque<KeyEventData> KeyEventDataQueue;
struct DemoMessageAndData {
DemoMsg message;
DemoMsgEventData data;
};
DemoMessageAndData PopDemoMessage()
{
DemoMessageAndData result;
result.message = DemoMessageQueue.front();
DemoMessageQueue.pop_front();
switch (result.message.type) {
case DemoMsg::MouseMotionEvent:
result.data.motion = MouseMotionEventDataQueue.front();
MouseMotionEventDataQueue.pop_front();
break;
case DemoMsg::MouseButtonDownEvent:
case DemoMsg::MouseButtonUpEvent:
result.data.button = MouseButtonEventDataQueue.front();
MouseButtonEventDataQueue.pop_front();
break;
case DemoMsg::MouseWheelEvent:
result.data.wheel = MouseWheelEventDataQueue.front();
MouseWheelEventDataQueue.pop_front();
break;
case DemoMsg::KeyDownEvent:
case DemoMsg::KeyUpEvent:
result.data.key = KeyEventDataQueue.front();
KeyEventDataQueue.pop_front();
break;
default:
break;
}
return result;
}
void ReadSettings(FILE *in, uint8_t version) // NOLINT(readability-identifier-length) void ReadSettings(FILE *in, uint8_t version) // NOLINT(readability-identifier-length)
{ {
DemoGraphicsWidth = ReadLE16(in); DemoGraphicsWidth = ReadLE16(in);
@ -277,39 +239,39 @@ void WriteSettings(FILE *out)
} }
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
bool CreateSdlEvent(const DemoMsg &dmsg, const DemoMsgEventData &data, SDL_Event &event, uint16_t &modState) bool CreateSdlEvent(const DemoMsg &dmsg, SDL_Event &event, uint16_t &modState)
{ {
const uint8_t type = dmsg.type; const uint8_t type = dmsg.type;
switch (type) { switch (type) {
case DemoMsg::MouseMotionEvent: case DemoMsg::MouseMotionEvent:
event.type = SDL_MOUSEMOTION; event.type = SDL_MOUSEMOTION;
event.motion.which = 0; event.motion.which = 0;
event.motion.x = data.motion.x; event.motion.x = dmsg.motion.x;
event.motion.y = data.motion.y; event.motion.y = dmsg.motion.y;
return true; return true;
case DemoMsg::MouseButtonDownEvent: case DemoMsg::MouseButtonDownEvent:
case DemoMsg::MouseButtonUpEvent: case DemoMsg::MouseButtonUpEvent:
event.type = type == DemoMsg::MouseButtonDownEvent ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP; event.type = type == DemoMsg::MouseButtonDownEvent ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
event.button.which = 0; event.button.which = 0;
event.button.button = data.button.button; event.button.button = dmsg.button.button;
event.button.state = type == DemoMsg::MouseButtonDownEvent ? SDL_PRESSED : SDL_RELEASED; event.button.state = type == DemoMsg::MouseButtonDownEvent ? SDL_PRESSED : SDL_RELEASED;
event.button.x = data.button.x; event.button.x = dmsg.button.x;
event.button.y = data.button.y; event.button.y = dmsg.button.y;
modState = data.button.mod; modState = dmsg.button.mod;
return true; return true;
case DemoMsg::MouseWheelEvent: case DemoMsg::MouseWheelEvent:
event.type = SDL_MOUSEWHEEL; event.type = SDL_MOUSEWHEEL;
event.wheel.which = 0; event.wheel.which = 0;
event.wheel.x = data.wheel.x; event.wheel.x = dmsg.wheel.x;
event.wheel.y = data.wheel.y; event.wheel.y = dmsg.wheel.y;
modState = data.wheel.mod; modState = dmsg.wheel.mod;
return true; return true;
case DemoMsg::KeyDownEvent: case DemoMsg::KeyDownEvent:
case DemoMsg::KeyUpEvent: case DemoMsg::KeyUpEvent:
event.type = type == DemoMsg::KeyDownEvent ? SDL_KEYDOWN : SDL_KEYUP; event.type = type == DemoMsg::KeyDownEvent ? SDL_KEYDOWN : SDL_KEYUP;
event.key.state = type == DemoMsg::KeyDownEvent ? SDL_PRESSED : SDL_RELEASED; event.key.state = type == DemoMsg::KeyDownEvent ? SDL_PRESSED : SDL_RELEASED;
event.key.keysym.sym = data.key.sym; event.key.keysym.sym = dmsg.key.sym;
event.key.keysym.mod = data.key.mod; event.key.keysym.mod = dmsg.key.mod;
return true; return true;
default: default:
if (type >= DemoMsg::MinCustomEvent) { if (type >= DemoMsg::MinCustomEvent) {
@ -370,43 +332,43 @@ uint8_t Sdl2ToSdl1MouseButton(uint8_t button)
} }
} }
bool CreateSdlEvent(const DemoMsg &dmsg, const DemoMsgEventData &data, SDL_Event &event, uint16_t &modState) bool CreateSdlEvent(const DemoMsg &dmsg, SDL_Event &event, uint16_t &modState)
{ {
const uint8_t type = dmsg.type; const uint8_t type = dmsg.type;
switch (type) { switch (type) {
case DemoMsg::MouseMotionEvent: case DemoMsg::MouseMotionEvent:
event.type = SDL_MOUSEMOTION; event.type = SDL_MOUSEMOTION;
event.motion.which = 0; event.motion.which = 0;
event.motion.x = data.motion.x; event.motion.x = dmsg.motion.x;
event.motion.y = data.motion.y; event.motion.y = dmsg.motion.y;
return true; return true;
case DemoMsg::MouseButtonDownEvent: case DemoMsg::MouseButtonDownEvent:
case DemoMsg::MouseButtonUpEvent: case DemoMsg::MouseButtonUpEvent:
event.type = type == DemoMsg::MouseButtonDownEvent ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP; event.type = type == DemoMsg::MouseButtonDownEvent ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
event.button.which = 0; event.button.which = 0;
event.button.button = Sdl2ToSdl1MouseButton(data.button.button); event.button.button = Sdl2ToSdl1MouseButton(dmsg.button.button);
event.button.state = type == DemoMsg::MouseButtonDownEvent ? SDL_PRESSED : SDL_RELEASED; event.button.state = type == DemoMsg::MouseButtonDownEvent ? SDL_PRESSED : SDL_RELEASED;
event.button.x = data.button.x; event.button.x = dmsg.button.x;
event.button.y = data.button.y; event.button.y = dmsg.button.y;
modState = data.button.mod; modState = dmsg.button.mod;
return true; return true;
case DemoMsg::MouseWheelEvent: case DemoMsg::MouseWheelEvent:
if (data.wheel.y == 0) { if (dmsg.wheel.y == 0) {
LogWarn("Demo: unsupported event (mouse wheel y == 0)"); LogWarn("Demo: unsupported event (mouse wheel y == 0)");
return false; return false;
} }
event.type = SDL_MOUSEBUTTONDOWN; event.type = SDL_MOUSEBUTTONDOWN;
event.button.which = 0; event.button.which = 0;
event.button.button = data.wheel.y > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN; event.button.button = dmsg.wheel.y > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN;
modState = data.wheel.mod; modState = dmsg.wheel.mod;
return true; return true;
case DemoMsg::KeyDownEvent: case DemoMsg::KeyDownEvent:
case DemoMsg::KeyUpEvent: case DemoMsg::KeyUpEvent:
event.type = type == DemoMsg::KeyDownEvent ? SDL_KEYDOWN : SDL_KEYUP; event.type = type == DemoMsg::KeyDownEvent ? SDL_KEYDOWN : SDL_KEYUP;
event.key.which = 0; event.key.which = 0;
event.key.state = type == DemoMsg::KeyDownEvent ? SDL_PRESSED : SDL_RELEASED; event.key.state = type == DemoMsg::KeyDownEvent ? SDL_PRESSED : SDL_RELEASED;
event.key.keysym.sym = Sdl2ToSdl1Key(data.key.sym); event.key.keysym.sym = Sdl2ToSdl1Key(dmsg.key.sym);
event.key.keysym.mod = static_cast<SDL_Keymod>(data.key.mod); event.key.keysym.mod = static_cast<SDL_Keymod>(dmsg.key.mod);
return true; return true;
default: default:
if (type >= DemoMsg::MinCustomEvent) { if (type >= DemoMsg::MinCustomEvent) {
@ -446,11 +408,11 @@ uint8_t MapPreV2DemoMsgEventType(uint16_t type)
} }
} }
void LogDemoMessage(const DemoMsg &msg, const DemoMsgEventData &data = DemoMsgEventData {}) void LogDemoMessage(const DemoMsg &dmsg)
{ {
#ifdef LOG_DEMOMODE_MESSAGES #ifdef LOG_DEMOMODE_MESSAGES
const uint8_t progressToNextGameTick = msg.progressToNextGameTick; const uint8_t progressToNextGameTick = dmsg.progressToNextGameTick;
switch (msg.type) { switch (dmsg.type) {
case DemoMsg::GameTick: case DemoMsg::GameTick:
#ifdef LOG_DEMOMODE_MESSAGES_GAMETICK #ifdef LOG_DEMOMODE_MESSAGES_GAMETICK
Log(" GameTick {:>3}", progressToNextGameTick); Log(" GameTick {:>3}", progressToNextGameTick);
@ -464,119 +426,122 @@ void LogDemoMessage(const DemoMsg &msg, const DemoMsgEventData &data = DemoMsgEv
case DemoMsg::MouseMotionEvent: case DemoMsg::MouseMotionEvent:
#ifdef LOG_DEMOMODE_MESSAGES_MOUSEMOTION #ifdef LOG_DEMOMODE_MESSAGES_MOUSEMOTION
Log("🖱 Message {:>3} MOUSEMOTION {} {}", progressToNextGameTick, Log("🖱 Message {:>3} MOUSEMOTION {} {}", progressToNextGameTick,
data.motion.x, data.motion.y); dmsg.motion.x, dmsg.motion.y);
#endif #endif
break; break;
case DemoMsg::MouseButtonDownEvent: case DemoMsg::MouseButtonDownEvent:
case DemoMsg::MouseButtonUpEvent: case DemoMsg::MouseButtonUpEvent:
Log("🖱 Message {:>3} {} {} {} {} 0x{:x}", progressToNextGameTick, Log("🖱 Message {:>3} {} {} {} {} 0x{:x}", progressToNextGameTick,
msg.type == DemoMsg::MouseButtonDownEvent ? "MOUSEBUTTONDOWN" : "MOUSEBUTTONUP", dmsg.type == DemoMsg::MouseButtonDownEvent ? "MOUSEBUTTONDOWN" : "MOUSEBUTTONUP",
data.button.button, data.button.x, data.button.y, data.button.mod); dmsg.button.button, dmsg.button.x, dmsg.button.y, dmsg.button.mod);
break; break;
case DemoMsg::MouseWheelEvent: case DemoMsg::MouseWheelEvent:
Log("🖱 Message {:>3} MOUSEWHEEL {} {} 0x{:x}", progressToNextGameTick, Log("🖱 Message {:>3} MOUSEWHEEL {} {} 0x{:x}", progressToNextGameTick,
data.wheel.x, data.wheel.y, data.wheel.mod); dmsg.wheel.x, dmsg.wheel.y, dmsg.wheel.mod);
break; break;
case DemoMsg::KeyDownEvent: case DemoMsg::KeyDownEvent:
case DemoMsg::KeyUpEvent: case DemoMsg::KeyUpEvent:
Log("🔤 Message {:>3} {} 0x{:x} 0x{:x}", progressToNextGameTick, Log("🔤 Message {:>3} {} 0x{:x} 0x{:x}", progressToNextGameTick,
msg.type == DemoMsg::KeyDownEvent ? "KEYDOWN" : "KEYUP", dmsg.type == DemoMsg::KeyDownEvent ? "KEYDOWN" : "KEYUP",
data.key.sym, data.key.mod); dmsg.key.sym, dmsg.key.mod);
break; break;
case DemoMsg::QuitEvent: case DemoMsg::QuitEvent:
Log("❎ Message {:>3} QUIT", progressToNextGameTick); Log("❎ Message {:>3} QUIT", progressToNextGameTick);
break; break;
default: default:
Log("📨 Message {:>3} USEREVENT {}", progressToNextGameTick, static_cast<uint8_t>(msg.type)); Log("📨 Message {:>3} USEREVENT {}", progressToNextGameTick, static_cast<uint8_t>(dmsg.type));
break; break;
} }
#endif // LOG_DEMOMODE_MESSAGES #endif // LOG_DEMOMODE_MESSAGES
} }
LoadingStatus LoadDemoMessages(int demoNumber) void CloseDemoFile()
{
if (DemoFile != nullptr) {
std::fclose(DemoFile);
DemoFile = nullptr;
}
}
LoadingStatus OpenDemoFile(int demoNumber)
{ {
CloseDemoFile();
const std::string path = StrCat(paths::PrefPath(), "demo_", demoNumber, ".dmo"); const std::string path = StrCat(paths::PrefPath(), "demo_", demoNumber, ".dmo");
FILE *demofile = OpenFile(path.c_str(), "rb"); DemoFile = OpenFile(path.c_str(), "rb");
if (demofile == nullptr) { if (DemoFile == nullptr) {
return LoadingStatus::FileNotFound; return LoadingStatus::FileNotFound;
} }
DemoFileVersion = ReadByte(DemoFile);
const uint8_t version = ReadByte(demofile); if (DemoFileVersion > Version) {
if (version > Version) {
return LoadingStatus::UnsupportedVersion; return LoadingStatus::UnsupportedVersion;
} }
DemoNumber = demoNumber;
gSaveNumber = ReadLE32(demofile); gSaveNumber = ReadLE32(DemoFile);
ReadSettings(demofile, version); ReadSettings(DemoFile, DemoFileVersion);
while (true) { return LoadingStatus::Success;
const uint8_t typeNum = version >= 2 ? ReadByte(demofile) : ReadLE32(demofile); }
if (std::feof(demofile) != 0)
break;
// Events with the high bit 1 are Rendering events with the rest of the bits used std::optional<DemoMsg> ReadDemoMessage()
// to encode `progressToNextGameTick` inline. {
if ((typeNum & 0b10000000) != 0) { const uint8_t typeNum = DemoFileVersion >= 2 ? ReadByte(DemoFile) : ReadLE32(DemoFile);
DemoMessageQueue.push_back(DemoMsg { DemoMsg::Rendering, static_cast<uint8_t>(typeNum & 0b01111111u) });
continue;
}
const uint8_t progressToNextGameTick = ReadByte(demofile);
switch (typeNum) { if (std::feof(DemoFile) != 0) {
case DemoMsg::GameTick: CloseDemoFile();
case DemoMsg::Rendering: return std::nullopt;
DemoMessageQueue.push_back(DemoMsg { static_cast<DemoMsg::EventType>(typeNum), progressToNextGameTick }); }
// Events with the high bit 1 are Rendering events with the rest of the bits used
// to encode `progressToNextGameTick` inline.
if ((typeNum & 0b10000000) != 0) {
DemoModeLastTick = SDL_GetTicks();
return DemoMsg { DemoMsg::Rendering, static_cast<uint8_t>(typeNum & 0b01111111u), {} };
}
const uint8_t progressToNextGameTick = ReadByte(DemoFile);
switch (typeNum) {
case DemoMsg::GameTick:
case DemoMsg::Rendering:
DemoModeLastTick = SDL_GetTicks();
return DemoMsg { static_cast<DemoMsg::EventType>(typeNum), progressToNextGameTick, {} };
default: {
const uint8_t eventType = DemoFileVersion >= 2 ? typeNum : MapPreV2DemoMsgEventType(static_cast<uint16_t>(ReadLE32(DemoFile)));
DemoMsg result { static_cast<DemoMsg::EventType>(eventType), progressToNextGameTick, {} };
switch (eventType) {
case DemoMsg::MouseMotionEvent: {
result.motion.x = ReadLE16(DemoFile);
result.motion.y = ReadLE16(DemoFile);
} break;
case DemoMsg::MouseButtonDownEvent:
case DemoMsg::MouseButtonUpEvent: {
result.button.button = ReadByte(DemoFile);
result.button.x = ReadLE16(DemoFile);
result.button.y = ReadLE16(DemoFile);
result.button.mod = ReadLE16(DemoFile);
} break;
case DemoMsg::MouseWheelEvent: {
result.wheel.x = DemoFileVersion >= 2 ? ReadLE16<int16_t>(DemoFile) : static_cast<int16_t>(ReadLE32<int32_t>(DemoFile));
result.wheel.y = DemoFileVersion >= 2 ? ReadLE16<int16_t>(DemoFile) : static_cast<int16_t>(ReadLE32<int32_t>(DemoFile));
result.wheel.mod = ReadLE16(DemoFile);
} break;
case DemoMsg::KeyDownEvent:
case DemoMsg::KeyUpEvent: {
result.key.sym = static_cast<SDL_Keycode>(ReadLE32(DemoFile));
result.key.mod = static_cast<SDL_Keymod>(ReadLE16(DemoFile));
} break;
case DemoMsg::QuitEvent: // SDL_QUIT
break; break;
default: { default:
const uint8_t eventType = version >= 2 ? typeNum : MapPreV2DemoMsgEventType(static_cast<uint16_t>(ReadLE32(demofile))); if (eventType < DemoMsg::MinCustomEvent) {
DemoMessageQueue.push_back(DemoMsg { static_cast<DemoMsg::EventType>(eventType), progressToNextGameTick }); app_fatal(StrCat("Unknown event ", eventType));
switch (eventType) {
case DemoMsg::MouseMotionEvent: {
MouseMotionEventData motion;
motion.x = ReadLE16(demofile);
motion.y = ReadLE16(demofile);
MouseMotionEventDataQueue.push_back(motion);
} break;
case DemoMsg::MouseButtonDownEvent:
case DemoMsg::MouseButtonUpEvent: {
MouseButtonEventData button;
button.button = ReadByte(demofile);
button.x = ReadLE16(demofile);
button.y = ReadLE16(demofile);
button.mod = ReadLE16(demofile);
MouseButtonEventDataQueue.push_back(button);
} break;
case DemoMsg::MouseWheelEvent: {
MouseWheelEventData wheel;
wheel.x = version >= 2 ? ReadLE16<int16_t>(demofile) : static_cast<int16_t>(ReadLE32<int32_t>(demofile));
wheel.y = version >= 2 ? ReadLE16<int16_t>(demofile) : static_cast<int16_t>(ReadLE32<int32_t>(demofile));
wheel.mod = ReadLE16(demofile);
MouseWheelEventDataQueue.push_back(wheel);
} break;
case DemoMsg::KeyDownEvent:
case DemoMsg::KeyUpEvent: {
KeyEventData key;
key.sym = static_cast<SDL_Keycode>(ReadLE32(demofile));
key.mod = static_cast<SDL_Keymod>(ReadLE16(demofile));
KeyEventDataQueue.push_back(key);
} break;
case DemoMsg::QuitEvent: // SDL_QUIT
break;
default:
if (eventType < DemoMsg::MinCustomEvent) {
app_fatal(StrCat("Unknown event ", eventType));
}
break;
} }
} break; break;
} }
DemoModeLastTick = SDL_GetTicks();
return result;
} break;
} }
std::fclose(demofile);
DemoModeLastTick = SDL_GetTicks();
return LoadingStatus::Success;
} }
void WriteDemoMsgHeader(DemoMsg::EventType type) void WriteDemoMsgHeader(DemoMsg::EventType type)
@ -595,11 +560,10 @@ namespace demo {
void InitPlayBack(int demoNumber, bool timedemo) void InitPlayBack(int demoNumber, bool timedemo)
{ {
DemoNumber = demoNumber;
Timedemo = timedemo; Timedemo = timedemo;
ControlMode = ControlTypes::KeyboardAndMouse; ControlMode = ControlTypes::KeyboardAndMouse;
const LoadingStatus status = LoadDemoMessages(demoNumber); const LoadingStatus status = OpenDemoFile(demoNumber);
switch (status) { switch (status) {
case LoadingStatus::Success: case LoadingStatus::Success:
return; return;
@ -672,10 +636,14 @@ bool IsRecording()
bool GetRunGameLoop(bool &drawGame, bool &processInput) bool GetRunGameLoop(bool &drawGame, bool &processInput)
{ {
if (DemoMessageQueue.empty()) if (CurrentDemoMessage == std::nullopt && DemoFile != nullptr)
CurrentDemoMessage = ReadDemoMessage();
if (CurrentDemoMessage == std::nullopt)
app_fatal("Demo queue empty"); app_fatal("Demo queue empty");
const DemoMsg &dmsg = DemoMessageQueue.front();
if (dmsg.isEvent()) const DemoMsg &dmsg = *CurrentDemoMessage;
if (CurrentDemoMessage->isEvent())
app_fatal("Unexpected event demo message in GetRunGameLoop"); app_fatal("Unexpected event demo message in GetRunGameLoop");
LogDemoMessage(dmsg); LogDemoMessage(dmsg);
if (Timedemo) { if (Timedemo) {
@ -706,7 +674,7 @@ bool GetRunGameLoop(bool &drawGame, bool &processInput)
} }
ProgressToNextGameTick = dmsg.progressToNextGameTick; ProgressToNextGameTick = dmsg.progressToNextGameTick;
const bool isGameTick = dmsg.type == DemoMsg::GameTick; const bool isGameTick = dmsg.type == DemoMsg::GameTick;
DemoMessageQueue.pop_front(); CurrentDemoMessage = std::nullopt;
if (isGameTick) if (isGameTick)
LogicTick++; LogicTick++;
return isGameTick; return isGameTick;
@ -724,7 +692,8 @@ bool FetchMessage(SDL_Event *event, uint16_t *modState)
return true; return true;
} }
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) { if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) {
DemoMessageQueue.clear(); CloseDemoFile();
CurrentDemoMessage = std::nullopt;
DemoNumber = -1; DemoNumber = -1;
Timedemo = false; Timedemo = false;
last_tick = SDL_GetTicks(); last_tick = SDL_GetTicks();
@ -741,15 +710,16 @@ bool FetchMessage(SDL_Event *event, uint16_t *modState)
} }
} }
if (!DemoMessageQueue.empty()) { if (CurrentDemoMessage == std::nullopt && DemoFile != nullptr)
if (DemoMessageQueue.front().isEvent()) { CurrentDemoMessage = ReadDemoMessage();
const DemoMessageAndData dmsg = PopDemoMessage(); if (CurrentDemoMessage != std::nullopt) {
LogDemoMessage(dmsg.message, dmsg.data); const DemoMsg &dmsg = *CurrentDemoMessage;
const bool hasEvent = CreateSdlEvent(dmsg.message, dmsg.data, *event, *modState); LogDemoMessage(dmsg);
ProgressToNextGameTick = dmsg.message.progressToNextGameTick; if (dmsg.isEvent()) {
const bool hasEvent = CreateSdlEvent(dmsg, *event, *modState);
ProgressToNextGameTick = dmsg.progressToNextGameTick;
CurrentDemoMessage = std::nullopt;
return hasEvent; return hasEvent;
} else {
LogDemoMessage(DemoMessageQueue.front());
} }
} }

11
Source/pfile.cpp

@ -27,6 +27,7 @@
#include "utils/language.h" #include "utils/language.h"
#include "utils/parse_int.hpp" #include "utils/parse_int.hpp"
#include "utils/paths.h" #include "utils/paths.h"
#include "utils/stdcompat/filesystem.hpp"
#include "utils/str_cat.hpp" #include "utils/str_cat.hpp"
#include "utils/str_split.hpp" #include "utils/str_split.hpp"
#include "utils/utf8.hpp" #include "utils/utf8.hpp"
@ -164,7 +165,17 @@ SaveWriter GetStashWriter()
void CopySaveFile(uint32_t saveNum, std::string targetPath) void CopySaveFile(uint32_t saveNum, std::string targetPath)
{ {
const std::string savePath = GetSavePath(saveNum); const std::string savePath = GetSavePath(saveNum);
#if defined(UNPACKED_SAVES)
#ifdef DVL_NO_FILESYSTEM
#error "UNPACKED_SAVES requires either DISABLE_DEMOMODE or C++17 <filesystem>"
#endif
CreateDir(targetPath.c_str());
for (const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(savePath)) {
CopyFileOverwrite(entry.path().string().c_str(), (targetPath + entry.path().filename().string()).c_str());
}
#else
CopyFileOverwrite(savePath.c_str(), targetPath.c_str()); CopyFileOverwrite(savePath.c_str(), targetPath.c_str());
#endif
} }
#endif #endif

Loading…
Cancel
Save