fix: crashes related to Keyboard in FreezeTagConfigMenu

In some kingdoms (e.g. in Sand, but not in Cascade) it always/sometimes crashed the game when closing the keyboard.

I assume that's because the Keyboard was on the SceneHeap which it shouldn't.
At least moving it to the game mode heap fixed it.

But then there was a memory leak that the game mode heap growed everytime the scene was reloaded.
That was because of some never freed memory in the Keyboard class.
I tried to implement destructors for `StageSceneStateServerConfig` and `FreezeTagConfigMenu` but that also crashed.
So I added new `clean()` methods instead.
This commit is contained in:
Robin C. Ladiges 2024-10-29 02:50:07 +01:00
parent 7fa8c659e1
commit be50d034f8
No known key found for this signature in database
GPG key ID: B494D3DF92661B99
8 changed files with 42 additions and 17 deletions

View file

@ -9,6 +9,7 @@ typedef void (*KeyboardSetup)(nn::swkbd::KeyboardConfig&);
class Keyboard { class Keyboard {
public: public:
Keyboard(ulong strSize); Keyboard(ulong strSize);
~Keyboard();
void keyboardThread(); void keyboardThread();
void openKeyboard(const char* initialText, KeyboardSetup setup); void openKeyboard(const char* initialText, KeyboardSetup setup);

View file

@ -41,6 +41,7 @@ class StageSceneStateServerConfig : public al::HostStateBase<al::Scene>, public
virtual void appear(void) override; virtual void appear(void) override;
virtual void kill(void) override; virtual void kill(void) override;
void clean();
void exeMainMenu(); void exeMainMenu();
void exeOpenKeyboardIP(); void exeOpenKeyboardIP();
void exeOpenKeyboardPort(); void exeOpenKeyboardPort();

View file

@ -9,6 +9,8 @@ class FreezeTagConfigMenu : public GameModeConfigMenu {
public: public:
FreezeTagConfigMenu(); FreezeTagConfigMenu();
void clean() override;
const sead::WFixedSafeString<0x200>* getStringData() override; const sead::WFixedSafeString<0x200>* getStringData() override;
GameModeConfigMenu::UpdateAction updateMenu(int selectIndex) override; GameModeConfigMenu::UpdateAction updateMenu(int selectIndex) override;
@ -17,6 +19,5 @@ class FreezeTagConfigMenu : public GameModeConfigMenu {
private: private:
static constexpr int mItemCount = 8; static constexpr int mItemCount = 8;
sead::SafeArray<sead::WFixedSafeString<0x200>, mItemCount>* mItems = nullptr; sead::SafeArray<sead::WFixedSafeString<0x200>, mItemCount>* mItems = nullptr;
Keyboard* mScoreKeyboard; Keyboard* mKeyboard;
Keyboard* mRoundKeyboard;
}; };

View file

@ -12,6 +12,8 @@ public:
GameModeConfigMenu() = default; GameModeConfigMenu() = default;
virtual void clean() {}
virtual UpdateAction updateMenu(int selectIndex) { return UpdateAction::NOOP; } virtual UpdateAction updateMenu(int selectIndex) { return UpdateAction::NOOP; }
virtual const sead::WFixedSafeString<0x200>* getStringData() { return nullptr; } virtual const sead::WFixedSafeString<0x200>* getStringData() { return nullptr; }

View file

@ -20,7 +20,13 @@ Keyboard::Keyboard(ulong strSize) : mResultString(strSize) {
mCustomizeDicSize = 0x400; mCustomizeDicSize = 0x400;
mCustomizeDicBuf = (char*)malloc(mCustomizeDicSize); mCustomizeDicBuf = (char*)malloc(mCustomizeDicSize);
}
Keyboard::~Keyboard() {
delete mThread;
free(mWorkBuf);
free(mTextCheckBuf);
free(mCustomizeDicBuf);
} }
void Keyboard::keyboardThread() { void Keyboard::keyboardThread() {
@ -48,7 +54,6 @@ void Keyboard::keyboardThread() {
} }
void Keyboard::openKeyboard(const char* initialText, KeyboardSetup setupFunc) { void Keyboard::openKeyboard(const char* initialText, KeyboardSetup setupFunc) {
mInitialText = initialText; mInitialText = initialText;
mSetupFunc = setupFunc; mSetupFunc = setupFunc;

View file

@ -119,6 +119,7 @@ void initStateHook(
) { ) {
thisPtr->mStateOption = new StageSceneStateOption(stateName, host, initInfo, footer, data, unkBool); thisPtr->mStateOption = new StageSceneStateOption(stateName, host, initInfo, footer, data, unkBool);
if (sceneStateServerConfig) { sceneStateServerConfig->clean(); }
sceneStateServerConfig = new StageSceneStateServerConfig("ServerConfig", host, initInfo, footer, data, unkBool); sceneStateServerConfig = new StageSceneStateServerConfig("ServerConfig", host, initInfo, footer, data, unkBool);
} }

View file

@ -1,6 +1,10 @@
#include "server/freeze-tag/FreezeTagConfigMenu.hpp" #include "server/freeze-tag/FreezeTagConfigMenu.hpp"
#include <cmath>
#include <stdint.h> #include <stdint.h>
#include "sead/basis/seadNew.h"
#include "sead/heap/seadHeap.h"
#include "server/gamemode/GameModeManager.hpp"
#include "nn/util.h" #include "nn/util.h"
FreezeTagConfigMenu::FreezeTagConfigMenu() : GameModeConfigMenu() { FreezeTagConfigMenu::FreezeTagConfigMenu() : GameModeConfigMenu() {
@ -14,13 +18,12 @@ FreezeTagConfigMenu::FreezeTagConfigMenu() : GameModeConfigMenu() {
mItems->mBuffer[6].copy(u"Cappy Collision (ON) "); mItems->mBuffer[6].copy(u"Cappy Collision (ON) ");
mItems->mBuffer[7].copy(u"Cappy Bounce (OFF) "); mItems->mBuffer[7].copy(u"Cappy Bounce (OFF) ");
mScoreKeyboard = new Keyboard(6); sead::Heap* heap = GameModeManager::instance()->getHeap();
mScoreKeyboard->setHeaderText(u"Set your Freeze Tag score"); mKeyboard = new (heap) Keyboard(6);
mScoreKeyboard->setSubText(u""); }
mRoundKeyboard = new Keyboard(3); void FreezeTagConfigMenu::clean() {
mRoundKeyboard->setHeaderText(u"Set length of rounds you start in minutes"); delete mKeyboard;
mRoundKeyboard->setSubText(u"This will be automatically sent to other players (2-60 minutes)");
} }
const sead::WFixedSafeString<0x200>* FreezeTagConfigMenu::getStringData() { const sead::WFixedSafeString<0x200>* FreezeTagConfigMenu::getStringData() {
@ -92,7 +95,9 @@ GameModeConfigMenu::UpdateAction FreezeTagConfigMenu::updateMenu(int selectIndex
char buf[5]; char buf[5];
nn::util::SNPrintf(buf, 5, "%u", oldScore); nn::util::SNPrintf(buf, 5, "%u", oldScore);
mScoreKeyboard->openKeyboard( mKeyboard->setHeaderText(u"Set your Freeze Tag score");
mKeyboard->setSubText(u"");
mKeyboard->openKeyboard(
buf, buf,
[](nn::swkbd::KeyboardConfig& config) { [](nn::swkbd::KeyboardConfig& config) {
config.keyboardMode = nn::swkbd::KeyboardMode::ModeNumeric; config.keyboardMode = nn::swkbd::KeyboardMode::ModeNumeric;
@ -103,12 +108,12 @@ GameModeConfigMenu::UpdateAction FreezeTagConfigMenu::updateMenu(int selectIndex
} }
); );
while (!mScoreKeyboard->isThreadDone()) { while (!mKeyboard->isThreadDone()) {
nn::os::YieldThread(); // allow other threads to run nn::os::YieldThread(); // allow other threads to run
} }
if (!mScoreKeyboard->isKeyboardCancelled()) { if (!mKeyboard->isKeyboardCancelled()) {
newScore = ::atoi(mScoreKeyboard->getResult()); newScore = std::atoi(mKeyboard->getResult());
} }
if (newScore != uint16_t(-1)) { if (newScore != uint16_t(-1)) {
@ -128,7 +133,9 @@ GameModeConfigMenu::UpdateAction FreezeTagConfigMenu::updateMenu(int selectIndex
char buf[3]; char buf[3];
nn::util::SNPrintf(buf, 3, "%u", oldTime); nn::util::SNPrintf(buf, 3, "%u", oldTime);
mRoundKeyboard->openKeyboard( mKeyboard->setHeaderText(u"Set length of rounds you start in minutes");
mKeyboard->setSubText(u"This will be automatically sent to other players (2-60 minutes)");
mKeyboard->openKeyboard(
buf, buf,
[](nn::swkbd::KeyboardConfig& config) { [](nn::swkbd::KeyboardConfig& config) {
config.keyboardMode = nn::swkbd::KeyboardMode::ModeNumeric; config.keyboardMode = nn::swkbd::KeyboardMode::ModeNumeric;
@ -139,17 +146,18 @@ GameModeConfigMenu::UpdateAction FreezeTagConfigMenu::updateMenu(int selectIndex
} }
); );
while (!mRoundKeyboard->isThreadDone()) { while (!mKeyboard->isThreadDone()) {
nn::os::YieldThread(); // allow other threads to run nn::os::YieldThread(); // allow other threads to run
} }
if (!mRoundKeyboard->isKeyboardCancelled()) { if (!mKeyboard->isKeyboardCancelled()) {
newTime = ::atoi(mRoundKeyboard->getResult()); newTime = std::atoi(mKeyboard->getResult());
} }
if (newTime != uint8_t(-1)) { if (newTime != uint8_t(-1)) {
FreezeTagInfo::mRoundLength = al::clamp(newTime, u8(2), u8(60)); FreezeTagInfo::mRoundLength = al::clamp(newTime, u8(2), u8(60));
} }
return UpdateAction::NOOP; return UpdateAction::NOOP;
} }
case 4: { case 4: {

View file

@ -139,6 +139,12 @@ al::MessageSystem* StageSceneStateServerConfig::getMessageSystem(void) const {
return mMsgSystem; return mMsgSystem;
} }
void StageSceneStateServerConfig::clean() {
for (int mode = 0; mode < GameModeFactory::getModeCount() - 1; mode++) {
mGamemodeConfigMenus[mode].mMenu->clean();
}
}
void StageSceneStateServerConfig::exeMainMenu() { void StageSceneStateServerConfig::exeMainMenu() {
if (al::isFirstStep(this)) { if (al::isFirstStep(this)) {
activateInput(); activateInput();