impl saving after ip/port change, add sceneobj headers, rework puppet info system a bit

This commit is contained in:
CraftyBoss 2022-07-05 12:45:22 -07:00
parent 063e20677f
commit b00540b1d9
18 changed files with 589 additions and 287 deletions

View File

@ -4,17 +4,19 @@
.PHONY: all clean starlight send
SMOVER ?= 100
BUILDVER ?= 99.37
IP ?= 10.0.0.221
BUILDVER ?= 101
BUILDVERSTR ?= 1.0.1
IP ?= 10.0.0.221 # ftp server ip (usually is switch's local IP)
DEBUGLOG ?= 0 # defaults to disable debug logger
SERVERIP ?= 0.0.0.0 # put debug logger server IP here
ISEMU ?= 0 # set to 1 to compile for emulators
PROJNAME ?= StarlightBase
all: starlight
starlight:
$(MAKE) all -f MakefileNSO SMOVER=$(SMOVER) BUILDVER=$(BUILDVER) DEBUGLOG=$(DEBUGLOG) SERVERIP=${SERVERIP}
$(MAKE) all -f MakefileNSO SMOVER=$(SMOVER) BUILDVERSTR=$(BUILDVERSTR) BUILDVER=$(BUILDVER) DEBUGLOG=$(DEBUGLOG) SERVERIP=${SERVERIP} EMU=${ISEMU}
$(MAKE) starlight_patch_$(SMOVER)/*.ips
mkdir -p starlight_patch_$(SMOVER)/atmosphere/exefs_patches/$(PROJNAME)/
@ -29,9 +31,9 @@ starlight_patch_$(SMOVER)/*.ips: patches/*.slpatch patches/configs/$(SMOVER).con
@rm -f starlight_patch_$(SMOVER)/*.ips
python3 scripts/genPatch.py $(SMOVER)
# builds project with the file structure used in the yuzu emulator
yuzu:
$(MAKE) all -f MakefileNSO SMOVER=$(SMOVER) BUILDVER=$(BUILDVER)
# builds project with the file structure and flags used for emulators
emu:
$(MAKE) all -f MakefileNSO SMOVER=$(SMOVER) BUILDVERSTR=$(BUILDVERSTR) BUILDVER=$(BUILDVER) EMU=1
$(MAKE) starlight_patch_$(SMOVER)/*.ips
mkdir -p starlight_patch_$(SMOVER)/yuzu/

View File

@ -44,7 +44,7 @@ ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec
CFLAGS := -g -Wall -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__ -DSMOVER=$(SMOVER) -O3 -DNNSDK -DSWITCH -DBUILDVER=$(BUILDVER) -DDEBUGLOG=$(DEBUGLOG) -DSERVERIP=$(SERVERIP)
CFLAGS += $(INCLUDE) -D__SWITCH__ -DSMOVER=$(SMOVER) -O3 -DNNSDK -DSWITCH -DBUILDVERSTR=$(BUILDVERSTR) -DBUILDVER=$(BUILDVER) -DDEBUGLOG=$(DEBUGLOG) -DSERVERIP=$(SERVERIP) -DEMU=$(EMU)
CXXFLAGS := $(CFLAGS) -fno-rtti -fomit-frame-pointer -fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables -std=gnu++20

View File

@ -12,6 +12,7 @@ class SocketBase {
SocketBase(const char *name);
virtual nn::Result init(const char * ip, u16 port) = 0;
virtual bool closeSocket();
const char *getStateChar();
u8 getLogState();
@ -19,7 +20,6 @@ class SocketBase {
void set_sock_flags(int flags);
bool closeSocket();
void setName(const char *name) {strcpy(sockName, name);};
u32 socket_errno;

View File

@ -0,0 +1,89 @@
#pragma once
#include "al/scene/ISceneObj.h"
#include "al/scene/SceneObjHolder.h"
#include "SceneObjs.h"
al::ISceneObj *sub_4C4300(int objIndex) {
switch (objIndex)
{
case 0:
return new AmiiboNpcDirector();
case 1:
return new BgmAnimeSyncDirector();
case 3:
return new CapManHeroDemoDirector();
case 4:
return new CapMessageDirector();
case 5:
return new CapMessageMoonNotifier();
case 7:
return new CoinCollectHolder();
case 8:
return new CoinCollectWatcher();
case 9:
return new CollectBgmPlayer();
case 11:
return new EchoEmitterHolder();
case 12:
return new ElectricWireCameraTicketHolder();
case 17:
return new FukankunZoomObjHolder();
case 21:
return new GrowPlantDirector();
case 22:
return new GuidePosInfoHolder();
case 23:
return new HintPhotoLayoutHolder();
case 26:
return new HtmlViewerRequester();
case 29:
return new KidsModeLayoutAccessor();
case 34:
return new LoginLotteryDirector();
case 36:
return new MoviePlayer();
case 39:
return new PaintObjHolder();
case 42:
return new PlayerStartInfoHolder();
case 44:
return new QuestInfoHolder(64);
case 49:
return new RandomItemSelector();
case 52:
return nullptr;
case 53:
return new RhyhtmInfoWatcher("");
case 55:
return new RouteGuideDirector();
case 56:
return new SceneEventNotifier();
case 60:
return new al::StageSyncCounter();
case 62:
return new TalkNpcParamHolder();
case 63:
return new TalkNpcSceneEventSwitcher();
case 64:
return new TestStageTimeDirector();
case 65:
return new TimeBalloonDirector();
case 70:
return new TsukkunTraceHolder();
case 71:
return new WipeHolderRequester();
case 72:
return new YoshiFruitWatcher();
case 73:
return new HelpAmiiboDirector();
default:
return nullptr;
}
}
class SceneObjFactory {
public:
al::SceneObjHolder *createSceneObjHolder(void) { return new al::SceneObjHolder(&sub_4C4300, 0x4A);}
};

View File

@ -4,9 +4,11 @@
namespace al {
typedef al::ISceneObj* (*SceneObjCreator)(int);
class SceneObjHolder {
public:
SceneObjHolder(al::ISceneObj* (*)(int), int);
SceneObjHolder(SceneObjCreator, int);
ISceneObj *tryGetObj(int) const; // unsafe get still
void setSceneObj(al::ISceneObj *,int);
@ -15,8 +17,14 @@ namespace al {
ISceneObj *getObj(int) const;
void create(int);
SceneObjCreator mObjCreator;
al::ISceneObj **mSceneObjs;
int mMaxObjCount;
};
static_assert(sizeof(SceneObjHolder) == 0x18, "SceneObjHolder Size");
class IUseSceneObjHolder
{
public:

View File

@ -0,0 +1,179 @@
#pragma once
#include "al/scene/ISceneObj.h"
#include "al/scene/SceneObjHolder.h"
#include "game/SceneObjs/RouteGuideDirector.h"
// temp header to cleanup SceneObjFactory
namespace al {
struct StageSyncCounter : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
}
struct AmiiboNpcDirector : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct BgmAnimeSyncDirector : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct CapManHeroDemoDirector : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct CapMessageDirector : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct CapMessageMoonNotifier : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct CoinCollectHolder : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct CoinCollectWatcher : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct CollectBgmPlayer : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct EchoEmitterHolder : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct ElectricWireCameraTicketHolder : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct FukankunZoomObjHolder : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct GrowPlantDirector : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct GuidePosInfoHolder : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct HintPhotoLayoutHolder : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct HtmlViewerRequester : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct KidsModeLayoutAccessor : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct LoginLotteryDirector : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct MoviePlayer : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct PaintObjHolder : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct PlayerStartInfoHolder : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct RandomItemSelector : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct SceneEventNotifier : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct TalkNpcParamHolder : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct TalkNpcSceneEventSwitcher : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct TestStageTimeDirector : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct TimeBalloonDirector : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct TsukkunTraceHolder : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct WipeHolderRequester : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct YoshiFruitWatcher : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct HelpAmiiboDirector : public al::ISceneObj {
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct QuestInfoHolder : public al::ISceneObj {
QuestInfoHolder(int);
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};
struct RhyhtmInfoWatcher : public al::ISceneObj {
RhyhtmInfoWatcher(const char*);
virtual const char* getSceneObjName() override;
virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
virtual void initSceneObj(void) override;
};

View File

@ -18,4 +18,5 @@ class GameDataHolderAccessor : public GameDataHolderWriter
public:
GameDataHolderAccessor(al::IUseSceneObjHolder const *IUseObjHolder) {mData = (GameDataHolder*)al::getSceneObj(IUseObjHolder, 18);}
GameDataHolderAccessor(al::SceneObjHolder const *objHolder) {mData = (GameDataHolder*)objHolder->getObj(18); }
GameDataHolderAccessor() {mData = nullptr; } // default ctor
};

View File

@ -0,0 +1,19 @@
#pragma once
#include "game/GameData/GameDataHolder.h"
namespace SaveDataAccessFunction {
void startSaveDataInit(GameDataHolder *);
void startSaveDataInitSync(GameDataHolder *);
void startSaveDataLoadFile(GameDataHolder *);
void startSaveDataReadSync(GameDataHolder *);
void startSaveDataReadAll(GameDataHolder *);
void startSaveDataWrite(GameDataHolder *);
void startSaveDataWriteWithWindow(GameDataHolder *);
void startSaveDataCopyWithWindow(GameDataHolder *,int,int);
void startSaveDataDeleteWithWindow(GameDataHolder *,int);
void startSaveDataWriteSync(GameDataHolder *);
bool updateSaveDataAccess(GameDataHolder *,bool);
bool isEnableSave(GameDataHolder const*);
bool isDoneSave(GameDataHolder *);
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "al/LiveActor/LiveActor.h"
#include "al/scene/ISceneObj.h"
class RouteGuideDirector : public al::LiveActor, public al::ISceneObj {
public:
RouteGuideDirector();
void initAfterPlacementSceneObj(al::ActorInitInfo const&) override;
bool isValidate(void) const;
void offGuideSystem(void);
void deactivateGuide(void);
void onGuideSystem(void);
void activateGuide(void);
void offGuideByActor(al::LiveActor *);
void addInvidateList(al::LiveActor *);
void onGuideByActor(al::LiveActor *);
void removeInvidateList(al::LiveActor const*);
void addRouteGuidePointBufferCount(int);
void registerRouteGuidePoint(struct RouteGuidePoint *);
void addRouteGuideArrowBufferCount(int);
void registerRouteGuideArrow(struct RouteGuideArrowBase*);
void exeOff(void);
void exeOn(void);
virtual const char* getSceneObjName() override;
virtual void initSceneObj(void) override;
};

View File

@ -44,6 +44,7 @@ class StageSceneStateServerConfig : public al::HostStateBase<al::Scene>, public
void exeRestartServer();
void exeGamemodeConfig();
void exeGamemodeSelect();
void exeSaveData();
void endSubMenu();
@ -82,4 +83,5 @@ namespace {
NERVE_HEADER(StageSceneStateServerConfig, RestartServer)
NERVE_HEADER(StageSceneStateServerConfig, GamemodeConfig)
NERVE_HEADER(StageSceneStateServerConfig, GamemodeSelect)
NERVE_HEADER(StageSceneStateServerConfig, SaveData)
}

View File

@ -33,7 +33,11 @@ namespace nn
return *this;
}
inline void print() {
inline bool isEmpty() const {
return *this == EmptyId;
}
inline void print() const {
Logger::log("Player ID: 0x");
Logger::disableName();
for (size_t i = 0; i < 0x10; i++) { Logger::log("%02X", data[i]); }
@ -41,13 +45,15 @@ namespace nn
Logger::enableName();
}
inline void print(const char *prefix) {
inline void print(const char *prefix) const {
Logger::log("%s: 0x", prefix);
Logger::disableName();
for (size_t i = 0; i < 0x10; i++) { Logger::log("%02X", data[i]); }
Logger::log("\n");
Logger::enableName();
}
static const Uid EmptyId;
};
typedef u64 NetworkServiceAccountId;

View File

@ -1,53 +0,0 @@
; Disable uploading error reports to Nintendo
[eupld]
; upload_enabled = u8!0x0
; Control whether RO should ease its validation of NROs.
; (note: this is normally not necessary, and ips patches can be used.)
[ro]
; ease_nro_restriction = u8!0x1
; Atmosphere custom settings
[atmosphere]
; Reboot from fatal automatically after some number of milliseconds.
; If field is not present or 0, fatal will wait indefinitely for user input.
; fatal_auto_reboot_interval = u64!0x0
; Make the power menu's "reboot" button reboot to payload.
; Set to "normal" for normal reboot, "rcm" for rcm reboot.
; power_menu_reboot_function = str!payload
; Controls whether dmnt cheats should be toggled on or off by
; default. 1 = toggled on by default, 0 = toggled off by default.
; dmnt_cheats_enabled_by_default = u8!0x1
; Controls whether dmnt should always save cheat toggle state
; for restoration on new game launch. 1 = always save toggles,
; 0 = only save toggles if toggle file exists.
; dmnt_always_save_cheat_toggles = u8!0x0
; Enable writing to BIS partitions for HBL.
; This is probably undesirable for normal usage.
; enable_hbl_bis_write = u8!0x0
; Enable reading the CAL0 partition for HBL.
; This is probably undesirable for normal usage.
; enable_hbl_cal_read = u8!0x0
; Controls whether fs.mitm should redirect save files
; to directories on the sd card.
; 0 = Do not redirect, 1 = Redirect.
; NOTE: EXPERIMENTAL
; If you do not know what you are doing, do not touch this yet.
; fsmitm_redirect_saves_to_sd = u8!0x0
; Controls whether to enable the deprecated hid mitm
; to fix compatibility with old homebrew.
; 0 = Do not enable, 1 = Enable.
; Please note this setting may be removed in a
; future release of Atmosphere.
; enable_deprecated_hid_mitm = u8!0x0
; Controls whether am sees system settings "DebugModeFlag" as
; enabled or disabled.
; 0 = Disabled (not debug mode), 1 = Enabled (debug mode)
; enable_am_debug_mode = u8!0x0
[hbloader]
; Controls the size of the homebrew heap when running as applet.
; If set to zero, all available applet memory is used as heap.
; The default is zero.
; applet_heap_size = u64!0x0
; Controls the amount of memory to reserve when running as applet
; for usage by other applets. This setting has no effect if
; applet_heap_size is non-zero. The default is 0x8600000.
; applet_heap_reservation_size = u64!0x8600000

View File

@ -24,6 +24,7 @@
#include "container/seadPtrArray.h"
#include "game/Actors/Shine.h"
#include "game/GameData/GameDataHolderAccessor.h"
#include "game/Player/PlayerActorHakoniwa.h"
#include "game/StageScene/StageScene.h"
#include "game/Layouts/CoinCounter.h"
@ -65,6 +66,8 @@
#include <stdlib.h>
#define MAXPUPINDEX 32
struct UIDIndexNode {
nn::account::Uid uid;
int puppetIndex;
@ -78,12 +81,12 @@ class Client {
Client(int bufferSize);
void init(al::LayoutInitInfo const &initInfo);
void init(al::LayoutInitInfo const &initInfo, GameDataHolderAccessor holder);
bool StartThreads();
void readFunc();
void recvFunc();
static void stopConnection();
static void restartConnection();
bool isDone() { return mReadThread->isDone(); };
static bool isSocketActive() { return sInstance ? sInstance->mSocket->isConnected() : false; };
@ -215,7 +218,7 @@ class Client {
void sendToStage(ChangeStagePacket* packet);
void disconnectPlayer(PlayerDC *packet);
int findPuppetID(const nn::account::Uid& id);
PuppetInfo* findPuppetInfo(const nn::account::Uid& id, bool isFindAvailable);
bool startConnection();
@ -225,7 +228,7 @@ class Client {
al::AsyncFunctorThread *mReadThread = nullptr; // TODO: use this thread to send any queued packets
// al::AsyncFunctorThread *mRecvThread; // TODO: use this thread to recieve packets and update PuppetInfo
sead::SafeArray<UIDIndexNode, 16> puppetPlayerID;
sead::SafeArray<UIDIndexNode, MAXPUPINDEX> puppetPlayerID;
int mConnectCount = 0;
@ -233,6 +236,8 @@ class Client {
sead::FixedSafeString<0x20> mUsername;
bool mIsConnectionActive = false;
// --- Server Syncing Members ---
// array of shine IDs for checking if multiple shines have been collected in quick sucession, all moons within the players stage that match the ID will be deleted
@ -277,6 +282,8 @@ class Client {
sead::FixedSafeString<0x40> mStageName;
GameDataHolderAccessor mHolder;
u8 mScenario = 0;
// --- Mode Info ---
@ -291,7 +298,7 @@ class Client {
// --- Puppet Info ---
PuppetInfo *mPuppetInfoArr[32];
PuppetInfo *mPuppetInfoArr[MAXPUPINDEX];
PuppetHolder *mPuppetHolder = nullptr;

View File

@ -22,7 +22,8 @@ class SocketClient : public SocketBase {
mPacketQueue = sead::PtrArray<Packet>();
mPacketQueue.tryAllocBuffer(maxBufSize, nullptr);
};
nn::Result init(const char * ip, u16 port) override;
nn::Result init(const char* ip, u16 port) override;
bool closeSocket() override;
bool SEND(Packet *packet);
bool RECV();
void printPacket(Packet* packet);

View File

@ -293,7 +293,7 @@ bool threadInit(HakoniwaSequence *mainSeq) { // hook for initializing client cl
al::initLayoutInitInfo(&lytInfo, mainSeq->mLytKit, 0, mainSeq->mAudioDirector, initInfo->mSystemInfo->mLayoutSys, initInfo->mSystemInfo->mMessageSys, initInfo->mSystemInfo->mGamePadSys);
Client::sInstance->init(lytInfo);
Client::sInstance->init(lytInfo, mainSeq->mGameDataHolder);
return GameDataFunction::isPlayDemoOpening(mainSeq->mGameDataHolder);
}

View File

@ -1,4 +1,5 @@
#include "server/Client.hpp"
#include <cmath>
#include <cstring>
#include "al/actor/ActorSceneInfo.h"
#include "al/layout/WindowConfirmWait.h"
@ -8,6 +9,7 @@
#include "game/GameData/GameDataHolderAccessor.h"
#include "game/Info/QuestInfo.h"
#include "game/Player/PlayerActorHakoniwa.h"
#include "game/SaveData/SaveDataAccessFunction.h"
#include "game/StageScene/StageScene.h"
#include "heap/seadHeapMgr.h"
#include "helpers.hpp"
@ -22,7 +24,9 @@
#include "packets/InitPacket.h"
#include "packets/Packet.h"
#include "packets/PlayerConnect.h"
#include "packets/PlayerDC.h"
#include "packets/TagInf.h"
#include "puppets/PuppetInfo.h"
#include "sead/basis/seadRawPrint.h"
#include "sead/math/seadQuat.h"
#include "server/gamemode/GameModeBase.hpp"
@ -62,7 +66,7 @@ Client::Client(int bufferSize) {
strcpy(mDebugPuppetInfo.puppetName, "PuppetDebug");
puppetPlayerID.fill({0, 0});
puppetPlayerID.fill({0});
mConnectCount = 0;
@ -83,9 +87,7 @@ Client::Client(int bufferSize) {
Logger::log("Player Name: %s\n", playerName.name);
#ifdef BUILDVER
Logger::log("%s Build Number: %s\n", playerName.name, TOSTRING(BUILDVER));
#endif
Logger::log("%s Build Number: %s\n", playerName.name, TOSTRING(BUILDVERSTR));
Logger::setLogName(playerName.name); // set Debug logger name to player name
@ -101,7 +103,7 @@ Client::Client(int bufferSize) {
*
* @param initInfo init info used to create layouts used by client
*/
void Client::init(al::LayoutInitInfo const &initInfo) {
void Client::init(al::LayoutInitInfo const &initInfo, GameDataHolderAccessor holder) {
mConnectionWait = new al::WindowConfirmWait("ServerWaitConnect", "WindowConfirmWait", initInfo);
@ -109,9 +111,9 @@ void Client::init(al::LayoutInitInfo const &initInfo) {
mConnectionWait->setTxtMessageConfirm(u"Failed to Connect!");
StartThreads();
mHolder = holder;
// mConnectionWait->tryEndForce();
StartThreads();
}
/**
@ -168,30 +170,43 @@ bool Client::StartThreads() {
* @brief restarts currently active connection to server
*
*/
void Client::stopConnection() {
void Client::restartConnection() {
if (!sInstance) {
Logger::log("Static Instance is null!\n");
return;
}
sInstance->mSocket->closeSocket();
Logger::log("Sending Disconnect.\n");
PlayerDC playerDC = PlayerDC();
playerDC.mUserID = sInstance->mUserID;
sInstance->mSocket->SEND(&playerDC);
if (sInstance->mSocket->closeSocket()) {
Logger::log("Sucessfully Closed Socket.\n");
}
sInstance->puppetPlayerID.fill({0,0});
sInstance->mConnectCount = 0;
sInstance->mSocket->init(sInstance->mServerIP.cstr(), sInstance->mServerPort);
sInstance->mIsConnectionActive = sInstance->mSocket->init(sInstance->mServerIP.cstr(), sInstance->mServerPort).isSuccess();
if(sInstance->mSocket->getLogState() == SOCKET_LOG_CONNECTED) {
Logger::log("Connected!\n");
Logger::log("Reconnect Sucessful!\n");
PlayerConnect initPacket;
initPacket.mUserID = sInstance->mUserID;
strcpy(initPacket.clientName, sInstance->mUsername.cstr());
initPacket.conType = ConnectionTypes::RECONNECT;
sInstance->mSocket->SEND(&initPacket);
} else {
Logger::log("Reconnect Unsuccessful.\n");
}
}
/**
@ -201,11 +216,15 @@ void Client::stopConnection() {
* @return false if connection was unable to establish
*/
bool Client::startConnection() {
bool isNeedSave = false;
if (mServerIP.isEmpty()) {
mKeyboard->setHeaderText(u"Save File does not contain an IP!");
mKeyboard->setSubText(u"Please set a Server IP Below.");
mServerIP = "0.0.0.0";
Client::openKeyboardIP();
isNeedSave = true;
}
if (!mServerPort) {
@ -213,11 +232,19 @@ bool Client::startConnection() {
mKeyboard->setSubText(u"Please set a Server Port Below.");
mServerPort = 1027;
Client::openKeyboardPort();
isNeedSave = true;
}
bool result = mSocket->init(mServerIP.cstr(), mServerPort).isSuccess();
if (isNeedSave) {
SaveDataAccessFunction::startSaveDataWrite(mHolder.mData);
}
mIsConnectionActive = mSocket->init(mServerIP.cstr(), mServerPort).isSuccess();
if (mIsConnectionActive) {
Logger::log("Sucessful Connection. Waiting to recieve init packet.\n");
if (result) {
// wait for client init packet
while (true) {
@ -234,19 +261,21 @@ bool Client::startConnection() {
}else {
Logger::log("First Packet was not Init!\n");
result = false;
mIsConnectionActive = false;
}
free(curPacket);
} else {
Logger::log("Recieve failed! Stopping Connection.\n");
mIsConnectionActive = false;
}
break;
}
}
return result;
return mIsConnectionActive;
}
/**
@ -331,6 +360,8 @@ void Client::readFunc() {
if (!startConnection()) {
Logger::log("Failed to Connect to Server.\n");
al::hidePane(mConnectionWait, "Page01"); // hide A button prompt since connection message automatically hides after 0.25 seconds
al::startAction(mConnectionWait, "Confirm", "State");
@ -342,16 +373,6 @@ void Client::readFunc() {
return;
}
if (isFirstConnect) {
sead::Heap* seqHeap = sead::HeapMgr::instance()->findHeapByName("SequenceHeap", 0);
if (seqHeap) {
Logger::log("Current Heap Name: %s\n", seqHeap->getName().cstr());
}
}
PlayerConnect initPacket;
initPacket.mUserID = mUserID;
strcpy(initPacket.clientName, mUsername.cstr());
@ -370,7 +391,7 @@ void Client::readFunc() {
isFirstConnect = false;
while(true) {
while(mIsConnectionActive) {
if (mSocket->getLogState() != SOCKET_LOG_CONNECTED) {
@ -383,14 +404,9 @@ void Client::readFunc() {
// if we ever disconnect, reset all our values until we reconnect
puppetPlayerID.fill({0,0});
mConnectCount = 0;
mSocket->closeSocket();
mSocket->init(mServerIP.cstr(), mServerPort);
if (mSocket->getLogState() == SOCKET_LOG_CONNECTED) {
if (mSocket->init(mServerIP.cstr(), mServerPort).isSuccess()) {
Logger::log("Connected!\n");
@ -399,12 +415,12 @@ void Client::readFunc() {
mConnectionWait->tryEnd();
continue;
} else {
Logger::log("Connection Failed! Retrying in 5 Seconds.\n");
}
nn::os::YieldThread(); // if we're currently waiting on the socket to be initialized, wait until it is
nn::os::SleepThread(nn::TimeSpan::FromSeconds(5));
// TODO: if a reconnect is sucessful, we should let the server know that this client has already connected, and that their player ID is already active
}
@ -474,11 +490,13 @@ void Client::readFunc() {
}
}else { // if false, socket has errored or disconnected, so close the socket and end this thread.
Logger::log("Client Socket Encountered an Error!\n");
Logger::log("Client Socket Encountered an Error! Errno: 0x%x\n", mSocket->socket_errno);
}
nn::os::YieldThread(); // allow other threads to run
}
Logger::log("Client Read Thread ending.\n");
}
/**
* @brief unused thread function for receiving thread
@ -753,61 +771,55 @@ void Client::sendShineCollectPacket(int shineID) {
* @param packet
*/
void Client::updatePlayerInfo(PlayerInf *packet) {
int puppetIndex = findPuppetID(packet->mUserID);
if(puppetIndex >= 0) {
PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex];
if (!curInfo) {
Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex);
return;
}
if(!curInfo->isConnected) {
curInfo->isConnected = true;
}
curInfo->playerPos = packet->playerPos;
// check if rotation is larger than zero and less than or equal to 1
if(abs(packet->playerRot.x) > 0.f || abs(packet->playerRot.y) > 0.f || abs(packet->playerRot.z) > 0.f || abs(packet->playerRot.w) > 0.f) {
if(abs(packet->playerRot.x) <= 1.f || abs(packet->playerRot.y) <= 1.f || abs(packet->playerRot.z) <= 1.f || abs(packet->playerRot.w) <= 1.f) {
curInfo->playerRot = packet->playerRot;
}
}
if (packet->actName != PlayerAnims::Type::Unknown) {
strcpy(curInfo->curAnimStr, PlayerAnims::FindStr(packet->actName));
} else {
strcpy(curInfo->curAnimStr, "Wait");
}
if(packet->subActName != PlayerAnims::Type::Unknown) {
strcpy(curInfo->curSubAnimStr, PlayerAnims::FindStr(packet->subActName));
} else {
strcpy(curInfo->curSubAnimStr, "");
}
curInfo->curAnim = packet->actName;
curInfo->curSubAnim = packet->subActName;
for (size_t i = 0; i < 6; i++)
{
// weights can only be between 0 and 1
if(packet->animBlendWeights[i] >= 0.f && packet->animBlendWeights[i] <= 1.f) {
curInfo->blendWeights[i] = packet->animBlendWeights[i];
}
}
//TEMP
if(!curInfo->isCapThrow) {
curInfo->capPos = packet->playerPos;
}
PuppetInfo* curInfo = findPuppetInfo(packet->mUserID, false);
if (!curInfo) {
return;
}
if(!curInfo->isConnected) {
curInfo->isConnected = true;
}
curInfo->playerPos = packet->playerPos;
// check if rotation is larger than zero and less than or equal to 1
if(abs(packet->playerRot.x) > 0.f || abs(packet->playerRot.y) > 0.f || abs(packet->playerRot.z) > 0.f || abs(packet->playerRot.w) > 0.f) {
if(abs(packet->playerRot.x) <= 1.f || abs(packet->playerRot.y) <= 1.f || abs(packet->playerRot.z) <= 1.f || abs(packet->playerRot.w) <= 1.f) {
curInfo->playerRot = packet->playerRot;
}
}
if (packet->actName != PlayerAnims::Type::Unknown) {
strcpy(curInfo->curAnimStr, PlayerAnims::FindStr(packet->actName));
} else {
strcpy(curInfo->curAnimStr, "Wait");
}
if(packet->subActName != PlayerAnims::Type::Unknown) {
strcpy(curInfo->curSubAnimStr, PlayerAnims::FindStr(packet->subActName));
} else {
strcpy(curInfo->curSubAnimStr, "");
}
curInfo->curAnim = packet->actName;
curInfo->curSubAnim = packet->subActName;
for (size_t i = 0; i < 6; i++)
{
// weights can only be between 0 and 1
if(packet->animBlendWeights[i] >= 0.f && packet->animBlendWeights[i] <= 1.f) {
curInfo->blendWeights[i] = packet->animBlendWeights[i];
}
}
//TEMP
if(!curInfo->isCapThrow) {
curInfo->capPos = packet->playerPos;
}
}
/**
@ -817,24 +829,15 @@ void Client::updatePlayerInfo(PlayerInf *packet) {
*/
void Client::updateHackCapInfo(HackCapInf *packet) {
int puppetIndex = findPuppetID(packet->mUserID);
PuppetInfo* curInfo = findPuppetInfo(packet->mUserID, false);
if(puppetIndex >= 0) {
if (curInfo) {
curInfo->capPos = packet->capPos;
curInfo->capRot = packet->capQuat;
PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex];
curInfo->isCapThrow = packet->isCapVisible;
if (curInfo) {
curInfo->capPos = packet->capPos;
curInfo->capRot = packet->capQuat;
curInfo->isCapThrow = packet->isCapVisible;
if (curInfo->capAnim && packet->capAnim) {
strcpy(curInfo->capAnim, packet->capAnim);
}
} else {
Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex);
}
strcpy(curInfo->capAnim, packet->capAnim);
}
}
@ -843,22 +846,18 @@ void Client::updateHackCapInfo(HackCapInf *packet) {
*
* @param packet
*/
void Client::updateCaptureInfo(CaptureInf *packet) {
int puppetIndex = findPuppetID(packet->mUserID);
if (puppetIndex >= 0) {
void Client::updateCaptureInfo(CaptureInf* packet) {
PuppetInfo* curInfo = findPuppetInfo(packet->mUserID, false);
PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex];
if (!curInfo) {
Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex);
return;
}
if (!curInfo) {
return;
}
curInfo->isCaptured = strlen(packet->hackName) > 0;
curInfo->isCaptured = strlen(packet->hackName) > 0;
if (curInfo->isCaptured) {
strcpy(curInfo->curHack, packet->hackName);
}
if (curInfo->isCaptured) {
strcpy(curInfo->curHack, packet->hackName);
}
}
@ -868,21 +867,15 @@ void Client::updateCaptureInfo(CaptureInf *packet) {
* @param packet
*/
void Client::updateCostumeInfo(CostumeInf *packet) {
if(packet->bodyModel && packet->capModel) {
int puppetIndex = findPuppetID(packet->mUserID);
if(puppetIndex >= 0) {
PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex];
if (!curInfo) {
Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex);
return;
}
PuppetInfo* curInfo = findPuppetInfo(packet->mUserID, false);
strcpy(curInfo->costumeBody, packet->bodyModel);
strcpy(curInfo->costumeHead, packet->capModel);
}
if (!curInfo) {
return;
}
strcpy(curInfo->costumeBody, packet->bodyModel);
strcpy(curInfo->costumeHead, packet->capModel);
}
/**
@ -902,18 +895,29 @@ void Client::updateShineInfo(ShineCollect* packet) {
*
* @param packet
*/
void Client::updatePlayerConnect(PlayerConnect *packet) {
int puppetIndex = findPuppetID(packet->mUserID);
if(puppetIndex >= 0) {
PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex];
void Client::updatePlayerConnect(PlayerConnect* packet) {
PuppetInfo* curInfo = findPuppetInfo(packet->mUserID, true);
if (!curInfo) {
return;
}
if (curInfo->isConnected) {
Logger::log("Info is already being used by another connected player!\n");
packet->mUserID.print("Connection ID");
curInfo->playerID.print("Target Info");
} else {
packet->mUserID.print("Player Connected! ID");
if (!curInfo) {
Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex);
return;
}
curInfo->playerID = packet->mUserID;
curInfo->isConnected = true;
strcpy(curInfo->puppetName, packet->clientName);
mConnectCount++;
}
}
/**
@ -922,26 +926,22 @@ void Client::updatePlayerConnect(PlayerConnect *packet) {
* @param packet
*/
void Client::updateGameInfo(GameInf *packet) {
int puppetIndex = findPuppetID(packet->mUserID);
if(puppetIndex >= 0) {
PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex];
PuppetInfo* curInfo = findPuppetInfo(packet->mUserID, false);
if (!curInfo) {
Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex);
return;
if (!curInfo) {
return;
}
if(curInfo->isConnected) {
curInfo->scenarioNo = packet->scenarioNo;
if(strcmp(packet->stageName, "") != 0 && strlen(packet->stageName) > 3) {
strcpy(curInfo->stageName, packet->stageName);
}
if(curInfo->isConnected) {
curInfo->scenarioNo = packet->scenarioNo;
if(strcmp(packet->stageName, "") != 0 && strlen(packet->stageName) > 3) {
strcpy(curInfo->stageName, packet->stageName);
}
curInfo->is2D = packet->is2D;
}
curInfo->is2D = packet->is2D;
}
}
@ -971,20 +971,15 @@ void Client::updateTagInfo(TagInf *packet) {
}
int puppetIndex = findPuppetID(packet->mUserID);
PuppetInfo* curInfo = findPuppetInfo(packet->mUserID, false);
if(puppetIndex >= 0) {
PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex];
if (!curInfo) {
Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex);
return;
}
curInfo->isIt = packet->isIt;
curInfo->seconds = packet->seconds;
curInfo->minutes = packet->minutes;
if (!curInfo) {
return;
}
curInfo->isIt = packet->isIt;
curInfo->seconds = packet->seconds;
curInfo->minutes = packet->minutes;
}
/**
@ -1011,29 +1006,20 @@ void Client::sendToStage(ChangeStagePacket* packet) {
* @param packet
*/
void Client::disconnectPlayer(PlayerDC *packet) {
int puppetIndex = findPuppetID(packet->mUserID);
if(puppetIndex >= 0) {
PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex];
PuppetInfo* curInfo = findPuppetInfo(packet->mUserID, false);
if (!curInfo) {
Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex);
return;
}
curInfo->isConnected = false;
curInfo->scenarioNo = -1;
strcpy(curInfo->stageName, "");
curInfo->isInSameStage = false;
mConnectCount--;
if (mConnectCount < 0) {
Logger::log("Connection Count went Negative!\n");
mConnectCount = 0;
}
if (!curInfo) {
return;
}
curInfo->isConnected = false;
curInfo->scenarioNo = -1;
strcpy(curInfo->stageName, "");
curInfo->isInSameStage = false;
mConnectCount--;
}
/**
@ -1064,23 +1050,27 @@ bool Client::isShineCollected(int shineId) {
* @param id
* @return int
*/
int Client::findPuppetID(const nn::account::Uid &id) {
for (size_t i = 0; i < this->puppetPlayerID.size(); i++)
{
if(this->puppetPlayerID[i].uid == id) {
return this->puppetPlayerID[i].puppetIndex;
PuppetInfo* Client::findPuppetInfo(const nn::account::Uid& id, bool isFindAvailable) {
PuppetInfo *firstAvailable = nullptr;
for (size_t i = 0; i < getMaxPlayerCount(); i++) {
PuppetInfo* curInfo = mPuppetInfoArr[i];
if (curInfo->playerID == id) {
return curInfo;
} else if (isFindAvailable && !firstAvailable && !curInfo->isConnected) {
firstAvailable = curInfo;
}
}
if(this->puppetPlayerID.size() > mConnectCount) {
int newIndex = mConnectCount;
this->puppetPlayerID[newIndex].puppetIndex = newIndex;
this->puppetPlayerID[newIndex].uid = id;
mConnectCount++;
return newIndex;
if (!firstAvailable) {
Logger::log("Unable to find Assigned Puppet for Player!\n");
id.print("User ID");
}
return -1;
return firstAvailable;
}
/**

View File

@ -2,6 +2,7 @@
#include <cstdlib>
#include <cstring>
#include "SocketBase.hpp"
#include "logger.hpp"
#include "nn/result.h"
#include "nn/socket.h"
@ -17,8 +18,7 @@ nn::Result SocketClient::init(const char* ip, u16 port) {
in_addr hostAddress = { 0 };
sockaddr serverAddress = { 0 };
if (socket_log_state != SOCKET_LOG_UNINITIALIZED && socket_log_state != SOCKET_LOG_DISCONNECTED)
return -1;
Logger::log("SocketClient::init: %s:%d sock %s\n", ip, port, getStateChar());
nn::nifm::Initialize();
nn::nifm::SubmitNetworkRequest();
@ -58,6 +58,7 @@ nn::Result SocketClient::init(const char* ip, u16 port) {
nn::Result result;
if((result = nn::socket::Connect(this->socket_log_socket, &serverAddress, sizeof(serverAddress))).isFailure()) {
Logger::log("Socket Connection Failed!\n");
this->socket_errno = nn::socket::GetLastErrno();
this->socket_log_state = SOCKET_LOG_UNAVAILABLE;
return result;
@ -78,7 +79,6 @@ bool SocketClient::SEND(Packet *packet) {
int valread = 0;
//Logger::log("Sending Packet Size: %d Sending Type: %s\n", packet->mPacketSize, packetNames[packet->mType]);
if ((valread = nn::socket::Send(this->socket_log_socket, buffer, packet->mPacketSize + sizeof(Packet), this->sock_flags) > 0)) {
return true;
@ -105,12 +105,15 @@ bool SocketClient::RECV() {
// read only the size of a header
while(valread < headerSize) {
int result = nn::socket::Recv(this->socket_log_socket, headerBuf + valread, headerSize - valread, this->sock_flags);
int result = nn::socket::Recv(this->socket_log_socket, headerBuf + valread,
headerSize - valread, this->sock_flags);
this->socket_errno = nn::socket::GetLastErrno();
if(result > 0) {
valread += result;
} else {
Logger::log("Header Read Failed! Value: %d Total Read: %d\n", result, valread);
this->socket_errno = nn::socket::GetLastErrno();
this->closeSocket();
return false;
}
@ -131,14 +134,16 @@ bool SocketClient::RECV() {
while (valread < fullSize) {
int result = nn::socket::Recv(this->socket_log_socket, packetBuf + valread, fullSize - valread, this->sock_flags);
int result = nn::socket::Recv(this->socket_log_socket, packetBuf + valread,
fullSize - valread, this->sock_flags);
this->socket_errno = nn::socket::GetLastErrno();
if (result > 0) {
valread += result;
}else {
} else {
free(packetBuf);
Logger::log("Packet Read Failed! Value: %d\nPacket Size: %d\nPacket Type: %s\n", result, header->mPacketSize, packetNames[header->mType]);
this->socket_errno = nn::socket::GetLastErrno();
this->closeSocket();
return false;
}
@ -151,11 +156,7 @@ bool SocketClient::RECV() {
} else {
free(packetBuf);
}
} else {
// Logger::log("Heap Allocation Failed! Returned nullptr\n");
}
} else {
// Logger::log("Recieved Unknown Packet Type! Size: %d\n", header->mPacketSize);
}
return true;
@ -183,3 +184,9 @@ void SocketClient::printPacket(Packet *packet) {
}
}
bool SocketClient::closeSocket() {
Logger::log("Closing Socket.\n");
return SocketBase::closeSocket();
}

View File

@ -1,6 +1,7 @@
#include "game/StageScene/StageSceneStateServerConfig.hpp"
#include <cstdlib>
#include <math.h>
#include "game/SaveData/SaveDataAccessFunction.h"
#include "server/Client.hpp"
#include "al/util.hpp"
#include "al/util/NerveUtil.h"
@ -172,7 +173,7 @@ void StageSceneStateServerConfig::exeOpenKeyboardIP() {
Client::openKeyboardIP();
// anything that happens after this will be ran after the keyboard closes
al::startHitReaction(mCurrentMenu, "リセット", 0);
al::setNerve(this, &nrvStageSceneStateServerConfigMainMenu);
al::setNerve(this, &nrvStageSceneStateServerConfigSaveData);
}
}
@ -183,23 +184,22 @@ void StageSceneStateServerConfig::exeOpenKeyboardPort() {
Client::getKeyboard()->setHeaderText(u"Set a Server Port Below.");
Client::getKeyboard()->setSubText(u"");
Client::openKeyboardIP();
Client::openKeyboardPort();
// anything that happens after this will be ran after the keyboard closes
al::startHitReaction(mCurrentMenu, "リセット", 0);
al::setNerve(this, &nrvStageSceneStateServerConfigMainMenu);
al::setNerve(this, &nrvStageSceneStateServerConfigSaveData);
}
}
void StageSceneStateServerConfig::exeRestartServer() {
if (al::isFirstStep(this)) {
mCurrentList->deactivate();
Client::stopConnection();
Client::restartConnection();
}
if (Client::isSocketActive()) {
//if (Client::isSocketActive()) {
al::startHitReaction(mCurrentMenu, "リセット", 0);
al::setNerve(this, &nrvStageSceneStateServerConfigMainMenu);
}
//}
}
void StageSceneStateServerConfig::exeGamemodeConfig() {
@ -291,6 +291,18 @@ void StageSceneStateServerConfig::subMenuUpdate() {
}
}
void StageSceneStateServerConfig::exeSaveData() {
if (al::isFirstStep(this)) {
SaveDataAccessFunction::startSaveDataWrite(mGameDataHolder);
}
if (SaveDataAccessFunction::updateSaveDataAccess(mGameDataHolder, false)) {
al::startHitReaction(mCurrentMenu, "リセット", 0);
al::setNerve(this, &nrvStageSceneStateServerConfigMainMenu);
}
}
namespace {
NERVE_IMPL(StageSceneStateServerConfig, MainMenu)
NERVE_IMPL(StageSceneStateServerConfig, OpenKeyboardIP)
@ -298,4 +310,5 @@ NERVE_IMPL(StageSceneStateServerConfig, OpenKeyboardPort)
NERVE_IMPL(StageSceneStateServerConfig, RestartServer)
NERVE_IMPL(StageSceneStateServerConfig, GamemodeConfig)
NERVE_IMPL(StageSceneStateServerConfig, GamemodeSelect)
NERVE_IMPL(StageSceneStateServerConfig, SaveData)
}