Skip to content

Commit 97a8fcb

Browse files
committed
v1.3.1 fixing trajectory glitch, and more
1 parent fe4dd2a commit 97a8fcb

16 files changed

Lines changed: 853 additions & 328 deletions

CMakeLists.txt

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,21 @@ target_include_directories(${PROJECT_NAME} PRIVATE
3838
${gd-imgui-cocos_SOURCE_DIR}/include
3939
)
4040

41+
if (MSVC)
42+
set_source_files_properties(
43+
src/audio/clicksounds.cpp
44+
src/audio/playsound.cpp
45+
src/gui/gui.cpp
46+
src/online/online_client.cpp
47+
src/render/renderer.cpp
48+
src/tools/layoutmode.cpp
49+
src/tools/speedhackaudio.cpp
50+
PROPERTIES
51+
SKIP_PRECOMPILE_HEADERS ON
52+
COMPILE_OPTIONS "$<$<CONFIG:Release>:/Od;/Ob0>"
53+
)
54+
endif()
55+
4156
if (NOT DEFINED ENV{GEODE_SDK})
4257
message(FATAL_ERROR "Unable to find Geode SDK! Please define GEODE_SDK environment variable to point to Geode")
4358
else()
@@ -49,6 +64,47 @@ add_subdirectory($ENV{GEODE_SDK} $ENV{GEODE_SDK}/build)
4964
CPMAddPackage("gh:maxnut/GDReplayFormat#9c32b5529067901418ea6e4dfbd36437dc8712bc")
5065
set(IMGUI_VERSION "v1.91.4" CACHE STRING "Pin imgui version" FORCE)
5166
CPMAddPackage("gh:matcool/gd-imgui-cocos#97643336209b760a3d59d83b770546b1cfef4e7d")
67+
set(GD_IMGUI_COCOS_BACKEND_FILE "${gd-imgui-cocos_SOURCE_DIR}/src/backend.cpp")
68+
file(READ "${GD_IMGUI_COCOS_BACKEND_FILE}" GD_IMGUI_COCOS_BACKEND_CONTENT)
69+
string(REPLACE
70+
"static const auto iniPath = (Mod::get()->getSaveDir() / \"imgui.ini\").string();"
71+
"static const auto iniPath = new std::string((Mod::get()->getSaveDir() / \"imgui.ini\").string());"
72+
GD_IMGUI_COCOS_BACKEND_CONTENT
73+
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
74+
)
75+
string(REPLACE
76+
"io.IniFilename = iniPath.c_str();"
77+
"io.IniFilename = iniPath->c_str();"
78+
GD_IMGUI_COCOS_BACKEND_CONTENT
79+
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
80+
)
81+
if (NOT GD_IMGUI_COCOS_BACKEND_CONTENT MATCHES "readClipboardText")
82+
string(REPLACE
83+
"#ifdef GEODE_IS_MOBILE"
84+
"#ifdef GEODE_IS_WINDOWS\nstatic std::string readClipboardText() {\n\tif (!OpenClipboard(nullptr)) return {};\n\n\tHANDLE hData = GetClipboardData(CF_UNICODETEXT);\n\tif (hData == nullptr) {\n\t\tCloseClipboard();\n\t\treturn {};\n\t}\n\n\tauto pszText = static_cast<wchar_t*>(GlobalLock(hData));\n\tif (pszText == nullptr) {\n\t\tCloseClipboard();\n\t\treturn {};\n\t}\n\n\tint size = WideCharToMultiByte(CP_UTF8, 0, pszText, -1, nullptr, 0, nullptr, nullptr);\n\tstd::string text;\n\tif (size > 0) {\n\t\ttext.resize(static_cast<size_t>(size));\n\t\tWideCharToMultiByte(CP_UTF8, 0, pszText, -1, text.data(), size, nullptr, nullptr);\n\t\tif (!text.empty() && text.back() == '\\0') {\n\t\t\ttext.pop_back();\n\t\t}\n\t}\n\n\tGlobalUnlock(hData);\n\tCloseClipboard();\n\treturn text;\n}\n#endif\n\n#ifdef GEODE_IS_MOBILE"
85+
GD_IMGUI_COCOS_BACKEND_CONTENT
86+
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
87+
)
88+
endif()
89+
string(REPLACE
90+
"auto static read = geode::utils::clipboard::read();"
91+
"auto static read = new std::string();"
92+
GD_IMGUI_COCOS_BACKEND_CONTENT
93+
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
94+
)
95+
string(REPLACE
96+
"read = geode::utils::clipboard::read();"
97+
"*read = readClipboardText();"
98+
GD_IMGUI_COCOS_BACKEND_CONTENT
99+
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
100+
)
101+
string(REPLACE
102+
"return read.c_str();"
103+
"return read->c_str();"
104+
GD_IMGUI_COCOS_BACKEND_CONTENT
105+
"${GD_IMGUI_COCOS_BACKEND_CONTENT}"
106+
)
107+
file(WRITE "${GD_IMGUI_COCOS_BACKEND_FILE}" "${GD_IMGUI_COCOS_BACKEND_CONTENT}")
52108
CPMAddPackage(
53109
NAME ZLIB
54110
GITHUB_REPOSITORY madler/zlib

src/ToastyReplay.hpp

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class ReplayEngine {
126126
int deferredReleaseA[2] = { -1, -1 };
127127
int deferredInputTick[2] = { -1, -1 };
128128
int deferredReleaseB[2][2] = { { -1, -1 }, { -1, -1 } };
129+
std::array<std::array<bool, 3>, 2> checkpointResumedHolds = {};
129130
bool activeButtons[6] = { false, false, false, false, false, false };
130131
bool priorButtonState[6] = { false, false, false, false, false, false };
131132
bool lateralInputPending[2] = { false, false };
@@ -168,79 +169,76 @@ class ReplayEngine {
168169
cbfMacros.clear();
169170
ttrMacros.clear();
170171
auto directory = geode::prelude::Mod::get()->getSaveDir() / "replays";
171-
if (!std::filesystem::exists(directory)) {
172+
std::error_code ec;
173+
if (!std::filesystem::exists(directory, ec) || ec) {
172174
return;
173175
}
174176

175-
for (auto& entry : std::filesystem::directory_iterator(directory)) {
176-
if (!entry.is_regular_file()) {
177+
auto markIncompatible = [&](std::string const& stem) {
178+
storedMacros.push_back(stem);
179+
incompatibleMacros.insert(stem);
180+
};
181+
182+
for (std::filesystem::directory_iterator it(directory, ec), end; !ec && it != end; it.increment(ec)) {
183+
if (!it->is_regular_file()) {
177184
continue;
178185
}
179186

180-
std::string stem = entry.path().stem().string();
181-
auto extension = entry.path().extension();
187+
auto path = it->path();
188+
auto stem = path.stem().string();
189+
auto extension = path.extension();
182190

183191
if (extension == ".ttr") {
184-
std::ifstream input(entry.path(), std::ios::binary);
185-
if (!input.is_open()) {
186-
storedMacros.push_back(stem);
187-
incompatibleMacros.insert(stem);
192+
auto bytes = ReplayStorage::readReplayBytes(path);
193+
if (!bytes || bytes->size() < 10) {
194+
markIncompatible(stem);
188195
continue;
189196
}
190197

191-
char header[10] = {};
192-
input.read(header, 10);
193-
input.close();
198+
auto const& header = *bytes;
194199
if (header[0] == 'T' && header[1] == 'T' && header[2] == 'R' && header[3] == '\0') {
195200
storedMacros.push_back(stem);
196201
ttrMacros.insert(stem);
197202
uint32_t flags = 0;
198-
std::memcpy(&flags, header + 6, sizeof(uint32_t));
203+
std::memcpy(&flags, header.data() + 6, sizeof(uint32_t));
199204
if ((flags & TTR_FLAG_ACCURACY_CBF) != 0) {
200205
cbfMacros.insert(stem);
201206
} else if ((flags & TTR_FLAG_ACCURACY_CBS) != 0) {
202207
cbsMacros.insert(stem);
203208
}
204209
} else {
205-
storedMacros.push_back(stem);
206-
incompatibleMacros.insert(stem);
210+
markIncompatible(stem);
207211
}
208212
continue;
209213
}
210214

211215
if (extension != ".gdr") {
212-
storedMacros.push_back(stem);
213-
incompatibleMacros.insert(stem);
216+
markIncompatible(stem);
214217
continue;
215218
}
216219

217-
std::ifstream input(entry.path(), std::ios::binary);
218-
if (!input.is_open()) {
219-
storedMacros.push_back(stem);
220-
incompatibleMacros.insert(stem);
220+
auto bytes = ReplayStorage::readReplayBytes(path);
221+
if (!bytes) {
222+
markIncompatible(stem);
221223
continue;
222224
}
223225

224-
input.seekg(0, std::ios::end);
225-
auto fileSize = input.tellg();
226-
input.seekg(0, std::ios::beg);
227-
std::vector<uint8_t> bytes(static_cast<size_t>(fileSize));
228-
input.read(reinterpret_cast<char*>(bytes.data()), fileSize);
229-
input.close();
230-
231226
try {
232-
MacroSequence temp = MacroSequence::importData(bytes);
227+
MacroSequence temp = MacroSequence::importData(*bytes);
233228
storedMacros.push_back(stem);
234229
if (temp.accuracyMode == AccuracyMode::CBF) {
235230
cbfMacros.insert(stem);
236231
} else if (temp.accuracyMode == AccuracyMode::CBS) {
237232
cbsMacros.insert(stem);
238233
}
239234
} catch (...) {
240-
storedMacros.push_back(stem);
241-
incompatibleMacros.insert(stem);
235+
markIncompatible(stem);
242236
}
243237
}
238+
239+
if (ec) {
240+
log::warn("Failed while scanning replay directory {}: {}", directory.string(), ec.message());
241+
}
244242
}
245243

246244
void beginCapture(GJGameLevel* level) {
@@ -536,6 +534,32 @@ class ReplayEngine {
536534
return rng;
537535
}
538536

537+
void clearCheckpointResumedHolds() {
538+
for (auto& playerHolds : checkpointResumedHolds) {
539+
playerHolds.fill(false);
540+
}
541+
}
542+
543+
bool isCheckpointHoldResumed(int playerSlot, int button) const {
544+
if (playerSlot < 0 || playerSlot >= static_cast<int>(checkpointResumedHolds.size())) {
545+
return false;
546+
}
547+
if (button < 1 || button > 3) {
548+
return false;
549+
}
550+
return checkpointResumedHolds[playerSlot][static_cast<size_t>(button - 1)];
551+
}
552+
553+
void setCheckpointHoldResumed(int playerSlot, int button, bool resumed) {
554+
if (playerSlot < 0 || playerSlot >= static_cast<int>(checkpointResumedHolds.size())) {
555+
return;
556+
}
557+
if (button < 1 || button > 3) {
558+
return;
559+
}
560+
checkpointResumedHolds[playerSlot][static_cast<size_t>(button - 1)] = resumed;
561+
}
562+
539563
Renderer renderer;
540564

541565
static ReplayEngine* get() {

src/audio/clicksounds.cpp

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@
55
#include <filesystem>
66
#include <algorithm>
77
#include <cmath>
8-
#include <unordered_map>
9-
#include <thread>
108
#include <chrono>
9+
#include <unordered_map>
1110

1211
using namespace geode::prelude;
1312

13+
namespace {
14+
FMOD::System* getAudioSystem() {
15+
auto* audio = FMODAudioEngine::sharedEngine();
16+
return audio ? audio->m_system : nullptr;
17+
}
18+
}
19+
1420
ClickSoundManager* ClickSoundManager::get() {
1521
static ClickSoundManager* instance = new ClickSoundManager();
1622
return instance;
@@ -29,9 +35,12 @@ std::filesystem::path ClickSoundManager::getClicksP2Dir() const {
2935
}
3036

3137
void ClickSoundManager::ensureChannelGroup() {
32-
if (!channelGroup) {
33-
FMODAudioEngine::sharedEngine()->m_system->createChannelGroup("ClickSounds", &channelGroup);
34-
}
38+
if (channelGroup) return;
39+
40+
auto* system = getAudioSystem();
41+
if (!system) return;
42+
43+
system->createChannelGroup("ClickSounds", &channelGroup);
3544
}
3645

3746
void ClickSoundManager::scanClickPacks() {
@@ -71,12 +80,7 @@ struct ResolvedClickSound {
7180
float volume = 0.0f;
7281
};
7382

74-
static std::vector<float> resampleInterleavedAudio(
75-
std::vector<float> const& input,
76-
int channels,
77-
int sourceRate,
78-
int targetRate
79-
) {
83+
static std::vector<float> resampleInterleavedAudio(std::vector<float> const& input, int channels, int sourceRate, int targetRate) {
8084
if (input.empty() || channels <= 0 || sourceRate <= 0 || targetRate <= 0 || sourceRate == targetRate) {
8185
return input;
8286
}
@@ -114,12 +118,7 @@ static std::string pickRandomFileWithRng(const std::vector<std::string>& files,
114118
}
115119

116120
template <class Rng>
117-
static ResolvedClickSound resolveClickSound(
118-
const ClickPack& pack,
119-
bool pressed,
120-
float softness,
121-
Rng& rng
122-
) {
121+
static ResolvedClickSound resolveClickSound(const ClickPack& pack, bool pressed, float softness, Rng& rng) {
123122
std::uniform_real_distribution<float> softDist(0.0f, 1.0f);
124123

125124
if (pressed) {
@@ -217,7 +216,7 @@ FMOD::Sound* ClickSoundManager::getCachedSound(const std::string& path) {
217216
auto it = soundCache.find(path);
218217
if (it != soundCache.end()) return it->second;
219218

220-
auto* system = FMODAudioEngine::sharedEngine()->m_system;
219+
auto* system = getAudioSystem();
221220
if (!system) return nullptr;
222221

223222
FMOD::Sound* sound = nullptr;
@@ -240,12 +239,16 @@ void ClickSoundManager::clearSoundCache() {
240239

241240
void ClickSoundManager::playFile(const std::string& path, float volume) {
242241
if (path.empty() || volume <= 0.0f) return;
242+
243243
ensureChannelGroup();
244+
if (!channelGroup) return;
245+
246+
auto* system = getAudioSystem();
247+
if (!system) return;
244248

245-
FMOD::Sound* sound = getCachedSound(path);
249+
auto* sound = getCachedSound(path);
246250
if (!sound) return;
247251

248-
auto* system = FMODAudioEngine::sharedEngine()->m_system;
249252
FMOD::Channel* channel = nullptr;
250253
system->playSound(sound, channelGroup, true, &channel);
251254
if (channel) {
@@ -258,9 +261,11 @@ void ClickSoundManager::playResolvedClick(bool pressed, bool isPlayer2) {
258261
if (!enabled) return;
259262

260263
auto* playLayer = PlayLayer::get();
261-
bool trueTwoPlayerMode = playLayer &&
262-
playLayer->m_levelSettings &&
263-
playLayer->m_levelSettings->m_twoPlayerMode;
264+
bool trueTwoPlayerMode = false;
265+
if (playLayer && playLayer->m_levelSettings) {
266+
trueTwoPlayerMode = playLayer->m_levelSettings->m_twoPlayerMode;
267+
}
268+
264269
ClickPack& pack = shouldUseP2Pack(isPlayer2, trueTwoPlayerMode) ? p2Pack : p1Pack;
265270
if (pack.empty()) return;
266271

@@ -320,16 +325,28 @@ void ClickSoundManager::clearPendingClicks() {
320325
pendingClicks.clear();
321326
}
322327

328+
void ClickSoundManager::shutdown() {
329+
clearSoundCache();
330+
decodedClickCache.clear();
331+
332+
if (channelGroup) {
333+
channelGroup->release();
334+
channelGroup = nullptr;
335+
}
336+
}
337+
323338
void ClickSoundManager::startBackgroundNoise() {
324339
stopBackgroundNoise();
325340
if (!backgroundNoiseEnabled || backgroundNoiseVolume <= 0.0f) return;
326341

327-
ClickPack& pack = p1Pack;
342+
auto* system = getAudioSystem();
343+
if (!system) return;
344+
345+
auto& pack = p1Pack;
328346
if (pack.noiseFiles.empty()) return;
329347

330348
ensureChannelGroup();
331-
auto* system = FMODAudioEngine::sharedEngine()->m_system;
332-
if (!system) return;
349+
if (!channelGroup) return;
333350

334351
std::string path = pack.noiseFiles[0];
335352
if (bgNoiseSound) { bgNoiseSound->release(); bgNoiseSound = nullptr; }
@@ -372,7 +389,9 @@ void ClickSoundManager::openClickFolderP2() {
372389

373390
std::vector<float> ClickSoundManager::decodeClickToRaw(const std::string& filePath, int targetSampleRate) {
374391
std::vector<float> result;
375-
auto* system = FMODAudioEngine::sharedEngine()->m_system;
392+
auto* system = getAudioSystem();
393+
if (!system) return result;
394+
376395
FMOD::Sound* sound = nullptr;
377396

378397
if (system->createSound(filePath.c_str(), FMOD_CREATESAMPLE, nullptr, &sound) != FMOD_OK || !sound)
@@ -394,7 +413,7 @@ std::vector<float> ClickSoundManager::decodeClickToRaw(const std::string& filePa
394413
sampleRate = static_cast<int>((static_cast<double>(lengthPcm) * 1000.0) / lengthMs);
395414
}
396415
if (sampleRate <= 0) {
397-
FMODAudioEngine::sharedEngine()->m_system->getSoftwareFormat(&sampleRate, nullptr, nullptr);
416+
system->getSoftwareFormat(&sampleRate, nullptr, nullptr);
398417
}
399418

400419
if (sampleRate <= 0 || channels <= 0) { sound->release(); return result; }

src/audio/clicksounds.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ struct ClickPack {
2626

2727
int hardCount() const { return static_cast<int>(hardClicks.size()); }
2828
int softCount() const { return static_cast<int>(softClicks.size()); }
29-
int releaseCount() const {
30-
return static_cast<int>(hardReleases.size() + softReleases.size() + releases.size());
31-
}
29+
int releaseCount() const { return static_cast<int>(hardReleases.size() + softReleases.size() + releases.size()); }
3230
int noiseCount() const { return static_cast<int>(noiseFiles.size()); }
3331
bool empty() const {
3432
return hardClicks.empty() && softClicks.empty() &&
@@ -69,6 +67,7 @@ class ClickSoundManager {
6967
void playClick(bool pressed, bool isPlayer2);
7068
void startBackgroundNoise();
7169
void stopBackgroundNoise();
70+
void shutdown();
7271
void updatePendingClicks();
7372
void clearPendingClicks();
7473
void openClickFolder();

0 commit comments

Comments
 (0)