From 36f9343f838505b2f933dccf40d16e303aa0e74c Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Mon, 5 Sep 2022 03:21:43 +0200 Subject: [PATCH 01/12] don't send empty GameInf and CostumeInf packets and resend them on reconnect Resend because: on server restarts the server will lose the stage and costume information. If only one client is connected to the server, the packets currently aren't resent, so the server doesn't know in which stage the client is and what costume it wears (which I'd like to display on the website). With more then one client connected it already works, because when another client joins the server, the client will send both packets. --- include/packets/GameInf.h | 4 +-- include/server/Client.hpp | 2 ++ include/server/SocketClient.hpp | 7 +++-- source/server/Client.cpp | 55 ++++++++++++++++++++++++--------- source/server/SocketClient.cpp | 11 +++++-- 5 files changed, 58 insertions(+), 21 deletions(-) diff --git a/include/packets/GameInf.h b/include/packets/GameInf.h index 34c5793..f435421 100644 --- a/include/packets/GameInf.h +++ b/include/packets/GameInf.h @@ -6,7 +6,7 @@ struct PACKED GameInf : Packet { GameInf() : Packet() {this->mType = PacketType::GAMEINF; mPacketSize = sizeof(GameInf) - sizeof(Packet);}; bool1 is2D = false; - u8 scenarioNo = -1; + u8 scenarioNo = 255; char stageName[0x40] = {}; bool operator==(const GameInf &rhs) const { @@ -19,4 +19,4 @@ struct PACKED GameInf : Packet { bool operator!=(const GameInf& rhs) const { return !operator==(rhs); } -}; \ No newline at end of file +}; diff --git a/include/server/Client.hpp b/include/server/Client.hpp index 16a2ca2..028fee9 100644 --- a/include/server/Client.hpp +++ b/include/server/Client.hpp @@ -103,6 +103,7 @@ class Client { static void sendShineCollectPacket(int shineId); static void sendTagInfPacket(); static void sendCaptureInfPacket(const PlayerActorHakoniwa *player); + void resendInitPackets(); int getCollectedShinesCount() { return curCollectedShines.size(); } int getShineID(int index) { if (index < curCollectedShines.size()) { return curCollectedShines[index]; } return -1; } @@ -226,6 +227,7 @@ class Client { // Backups for our last player/game packets, used for example to re-send them for newly connected clients PlayerInf lastPlayerInfPacket = PlayerInf(); GameInf lastGameInfPacket = GameInf(); + GameInf emptyGameInfPacket = GameInf(); CostumeInf lastCostumeInfPacket = CostumeInf(); Keyboard* mKeyboard = nullptr; // keyboard for setting server IP diff --git a/include/server/SocketClient.hpp b/include/server/SocketClient.hpp index 2f6dd74..217b292 100644 --- a/include/server/SocketClient.hpp +++ b/include/server/SocketClient.hpp @@ -20,9 +20,11 @@ #include "packets/Packet.h" +class Client; + class SocketClient : public SocketBase { public: - SocketClient(const char *name, sead::Heap *heap); + SocketClient(const char* name, sead::Heap* heap, Client* client); nn::Result init(const char* ip, u16 port) override; bool tryReconnect(); bool closeSocket() override; @@ -54,6 +56,7 @@ class SocketClient : public SocketBase { private: sead::Heap* mHeap = nullptr; + Client* client = nullptr; al::AsyncFunctorThread* mRecvThread = nullptr; al::AsyncFunctorThread* mSendThread = nullptr; @@ -72,4 +75,4 @@ class SocketClient : public SocketBase { bool stringToIPAddress(const char* str, in_addr* out); }; -typedef void (SocketClient::*SocketThreadFunc)(void); \ No newline at end of file +typedef void (SocketClient::*SocketThreadFunc)(void); diff --git a/source/server/Client.cpp b/source/server/Client.cpp index 9f8f1c4..4457ef0 100644 --- a/source/server/Client.cpp +++ b/source/server/Client.cpp @@ -27,7 +27,7 @@ Client::Client() { mKeyboard = new Keyboard(nn::swkbd::GetRequiredStringBufferSize()); - mSocket = new SocketClient("SocketClient", mHeap); + mSocket = new SocketClient("SocketClient", mHeap, this); mPuppetHolder = new PuppetHolder(maxPuppets); @@ -87,6 +87,7 @@ void Client::init(al::LayoutInitInfo const &initInfo, GameDataHolderAccessor hol Logger::log("Heap Free Size: %f/%f\n", mHeap->getFreeSize() * 0.001f, mHeap->getSize() * 0.001f); } + /** * @brief starts client read thread * @@ -103,6 +104,7 @@ bool Client::startThread() { return false; } } + /** * @brief restarts currently active connection to server * @@ -140,6 +142,7 @@ void Client::restartConnection() { Logger::log("Reconnect Unsuccessful.\n"); } } + /** * @brief starts a connection using client's TCP socket class, pulling up the software keyboard for user inputted IP if save file does not have one saved. * @@ -346,16 +349,18 @@ void Client::readFunc() { // Send relevant info packets when another client is connected - // Assume game packets are empty from first connection - if (lastGameInfPacket.mUserID != mUserID) - lastGameInfPacket.mUserID = mUserID; - mSocket->send(&lastGameInfPacket); + if (lastGameInfPacket != emptyGameInfPacket) { + // Assume game packets are empty from first connection + if (lastGameInfPacket.mUserID != mUserID) + lastGameInfPacket.mUserID = mUserID; + mSocket->send(&lastGameInfPacket); + } - // No need to send player/costume packets if they're empty - if (lastPlayerInfPacket.mUserID == mUserID) - mSocket->send(&lastPlayerInfPacket); - if (lastCostumeInfPacket.mUserID == mUserID) - mSocket->send(&lastCostumeInfPacket); + // No need to send player/costume packets if they're empty + if (lastPlayerInfPacket.mUserID == mUserID) + mSocket->send(&lastPlayerInfPacket); + if (lastCostumeInfPacket.mUserID == mUserID) + mSocket->send(&lastCostumeInfPacket); break; case PacketType::COSTUMEINF: @@ -479,6 +484,7 @@ void Client::sendPlayerInfPacket(const PlayerActorBase *playerBase, bool isYukim } } + /** * @brief sends info related to player's cap actor to server * @@ -553,14 +559,14 @@ void Client::sendGameInfPacket(const PlayerActorHakoniwa* player, GameDataHolder strcpy(packet->stageName, GameDataFunction::getCurrentStageName(holder)); - if(*packet != sInstance->lastGameInfPacket) { + if (*packet != sInstance->lastGameInfPacket && *packet != sInstance->emptyGameInfPacket) { sInstance->lastGameInfPacket = *packet; sInstance->mSocket->queuePacket(packet); } else { sInstance->mHeap->free(packet); // free packet if we're not using it } - } + /** * @brief * Sends only stage info to the server. @@ -584,9 +590,10 @@ void Client::sendGameInfPacket(GameDataHolderAccessor holder) { strcpy(packet->stageName, GameDataFunction::getCurrentStageName(holder)); - sInstance->lastGameInfPacket = *packet; - - sInstance->mSocket->queuePacket(packet); + if (*packet != sInstance->emptyGameInfPacket) { + sInstance->lastGameInfPacket = *packet; + sInstance->mSocket->queuePacket(packet); + } } /** @@ -637,6 +644,8 @@ void Client::sendCostumeInfPacket(const char* body, const char* cap) { return; } + if (!strcmp(body, "") && !strcmp(cap, "")) { return; } + sead::ScopedCurrentHeapSetter setter(sInstance->mHeap); CostumeInf *packet = new CostumeInf(body, cap); @@ -674,6 +683,21 @@ void Client::sendCaptureInfPacket(const PlayerActorHakoniwa* player) { } } +/** + * @brief + */ +void Client::resendInitPackets() { + // CostumeInfPacket + if (lastCostumeInfPacket.mUserID == mUserID) { + mSocket->queuePacket(&lastCostumeInfPacket); + } + + // GameInfPacket + if (lastGameInfPacket != emptyGameInfPacket) { + mSocket->queuePacket(&lastGameInfPacket); + } +} + /** * @brief * @@ -858,6 +882,7 @@ void Client::updatePlayerConnect(PlayerConnect* packet) { mConnectCount++; } } + /** * @brief * diff --git a/source/server/SocketClient.cpp b/source/server/SocketClient.cpp index 08d9349..2389cc7 100644 --- a/source/server/SocketClient.cpp +++ b/source/server/SocketClient.cpp @@ -13,7 +13,9 @@ #include "thread/seadMessageQueue.h" #include "types.h" -SocketClient::SocketClient(const char* name, sead::Heap* heap) : mHeap(heap), SocketBase(name) { +SocketClient::SocketClient(const char* name, sead::Heap* heap, Client* client) : mHeap(heap), SocketBase(name) { + + this->client = client; mRecvThread = new al::AsyncFunctorThread("SocketRecvThread", al::FunctorV0M(this, &SocketClient::recvFunc), 0, 0x1000, {0}); mSendThread = new al::AsyncFunctorThread("SocketSendThread", al::FunctorV0M(this, &SocketClient::sendFunc), 0, 0x1000, {0}); @@ -99,6 +101,11 @@ nn::Result SocketClient::init(const char* ip, u16 port) { send(&initPacket); + // on a reconnect, resend some maybe missing packets + if (initPacket.conType == ConnectionTypes::RECONNECT) { + client->resendInitPackets(); + } + return result; } @@ -357,4 +364,4 @@ void SocketClient::trySendQueue() { Packet* SocketClient::tryGetPacket(sead::MessageQueue::BlockType blockType) { return socket_log_state == SOCKET_LOG_CONNECTED ? (Packet*)mRecvQueue.pop(blockType) : nullptr; -} \ No newline at end of file +} From 6a5b5146b8848d9be5c62a3fb6e09fe7a950a73f Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Fri, 21 Oct 2022 00:04:08 +0200 Subject: [PATCH 02/12] use devkitpro docker image installing it via pacman everytime is unsupported and will lead to http 403 errors. --- Dockerfile | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7cf9951..b2f0afa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,26 +1,13 @@ -FROM ubuntu:20.04 as builder +FROM devkitpro/devkita64:latest as builder # install dependencies RUN apt-get update \ && apt-get install -y \ - curl \ - apt-transport-https \ python3 \ python3-pip \ - && pip install keystone-engine \ -; - -# install devkitpro -RUN ln -s /proc/self/mounts /etc/mtab \ - && mkdir /devkitpro/ \ - && echo "deb [signed-by=/devkitpro/pub.gpg] https://apt.devkitpro.org stable main" >/etc/apt/sources.list.d/devkitpro.list \ - && curl --fail -o /devkitpro/pub.gpg https://apt.devkitpro.org/devkitpro-pub.gpg \ - && apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install -y devkitpro-pacman \ - && dkp-pacman --noconfirm -S switch-dev \ + && pip3 install keystone-engine \ ; WORKDIR /app/ -ENV DEVKITPRO /opt/devkitpro ENTRYPOINT make From 7f6df57f4ac0d870ee2f6057f7eaf406cd876387 Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Fri, 21 Oct 2022 00:06:52 +0200 Subject: [PATCH 03/12] patch out deprecated ::set-output ::set-output currently triggers deprecation warnings and will stop working at 2023-06-01. https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ --- .github/actions/build/action.yml | 4 ++-- .github/workflows/dev-release.yml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 78a7f1a..5aa2a0a 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -31,8 +31,8 @@ runs: shell : bash run: | VERS=${{ inputs.tag }} - echo "::set-output name=version::${VERS:1}" - echo "::set-output name=filename::${{ inputs.prefix }}SMO_Online${{ (inputs.tag != '' && format('_{0}', inputs.tag)) || '' }}_for_${{ inputs.emu }}" + echo "version=${VERS:1}" >>$GITHUB_OUTPUT + echo "filename=${{ inputs.prefix }}SMO_Online${{ (inputs.tag != '' && format('_{0}', inputs.tag)) || '' }}_for_${{ inputs.emu }}" >>$GITHUB_OUTPUT - name : Set up Docker Buildx uses : docker/setup-buildx-action@v2 diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index d8aea1a..92bb0aa 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -35,7 +35,7 @@ jobs: id : env shell : bash run: | - echo "::set-output name=prefix::$(date +'%Y%m%d_%H%M%S_' --utc)" + echo "prefix=$(date +'%Y%m%d_%H%M%S_' --utc)" >>$GITHUB_OUTPUT - name : Build artifacts id : build @@ -48,7 +48,7 @@ jobs: id : set-output shell : bash run : | - echo "::set-output name=filename-${{ matrix.emu }}::${{ steps.build.outputs.filename }}" + echo "filename-${{ matrix.emu }}=${{ steps.build.outputs.filename }}" >>$GITHUB_OUTPUT attach: needs: build @@ -63,7 +63,7 @@ jobs: shell : bash run: | url=`curl -sS --fail ${{ github.api_url }}/repos/${{ github.repository }}/releases/tags/${{ env.TAG }} | jq .upload_url -r` - echo "::set-output name=upload_url::$url" + echo "upload_url=$url" >>$GITHUB_OUTPUT - name : Attach build artifacts to release (Switch) uses : ./.github/actions/attach From f60fc956d638a555a14027dc595e8c150d12cfc2 Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Fri, 21 Oct 2022 00:08:09 +0200 Subject: [PATCH 04/12] for Emulators => for Ryujinx & for yuzu --- .github/actions/build/action.yml | 15 +++++++++++++-- .github/workflows/build.yml | 2 +- .github/workflows/dev-release.yml | 14 +++++++++++--- .github/workflows/release.yml | 14 +++++++++++--- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 5aa2a0a..1524ee4 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -11,7 +11,7 @@ inputs: required : false default : '' emu: - description : 'what system the build is for, Switch or Emulators' + description : 'what system the build is for: Switch, Ryujinx or yuzu' required : false default : 'Switch' @@ -56,11 +56,22 @@ runs: docker run --rm \ -u `id -u`:`id -g` \ -v "/$PWD/":/app/ \ - -e ISEMU=${{ (inputs.emu == 'Emulators' && '1') || '0' }} \ + -e ISEMU=${{ (inputs.emu != 'Switch' && '1') || '0' }} \ ${{ (steps.env.outputs.version != '' && format('-e BUILDVER={0}', steps.env.outputs.version)) || '' }} \ smoo-build-env \ ; cp -r ./romfs/ ./starlight_patch_100/atmosphere/contents/0100000000010000/. + - + name : Yuzu + shell : bash + if : ${{ inputs.emu == 'yuzu' }} + run: | + cd ./starlight_patch_100/ + mkdir ./SMOO/ + mv ./atmosphere/contents/0100000000010000/exefs ./SMOO/exefs + mv ./atmosphere/contents/0100000000010000/romfs ./SMOO/romfs + mv ./atmosphere/exefs_patches/StarlightBase/3CA12DFAAF9C82DA064D1698DF79CDA1.ips ./SMOO/romfs/ + rm -rf ./atmosphere/ - name : Upload artifacts uses : actions/upload-artifact@v3 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 25259ca..76179ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: build: strategy: matrix: - emu : [ Switch, Emulators ] + emu : [ Switch, Ryujinx, yuzu ] runs-on: ubuntu-latest steps: - diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index 92bb0aa..000ffea 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -21,11 +21,12 @@ jobs: build: strategy: matrix: - emu : [ Switch, Emulators ] + emu : [ Switch, Ryujinx, yuzu ] runs-on: ubuntu-latest outputs: filename1: ${{ steps.set-output.outputs.filename-Switch }} - filename2: ${{ steps.set-output.outputs.filename-Emulators }} + filename2: ${{ steps.set-output.outputs.filename-Ryujinx }} + filename3: ${{ steps.set-output.outputs.filename-yuzu }} steps: - name : Checkout @@ -72,12 +73,19 @@ jobs: upload_url : ${{ steps.release.outputs.upload_url }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} - - name : Attach build artifacts to release (Emulators) + name : Attach build artifacts to release (Ryujinx) uses : ./.github/actions/attach with: filename : ${{ needs.build.outputs.filename2 }} upload_url : ${{ steps.release.outputs.upload_url }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} + - + name : Attach build artifacts to release (yuzu) + uses : ./.github/actions/attach + with: + filename : ${{ needs.build.outputs.filename3 }} + upload_url : ${{ steps.release.outputs.upload_url }} + GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} move: needs: attach diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1d72252..859a4d7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,11 +14,12 @@ jobs: build: strategy: matrix: - emu : [ Switch, Emulators ] + emu : [ Switch, Ryujinx, yuzu ] runs-on: ubuntu-latest outputs: filename1: ${{ steps.set-output.outputs.filename-Switch }} - filename2: ${{ steps.set-output.outputs.filename-Emulators }} + filename2: ${{ steps.set-output.outputs.filename-Ryujinx }} + filename3: ${{ steps.set-output.outputs.filename-yuzu }} steps: - name : Checkout @@ -59,9 +60,16 @@ jobs: upload_url : ${{ steps.release.outputs.upload_url }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} - - name : Attach build artifacts to release (Emulators) + name : Attach build artifacts to release (Ryujinx) uses : ./.github/actions/attach with: filename : ${{ needs.build.outputs.filename2 }} upload_url : ${{ steps.release.outputs.upload_url }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} + - + name : Attach build artifacts to release (yuzu) + uses : ./.github/actions/attach + with: + filename : ${{ needs.build.outputs.filename3 }} + upload_url : ${{ steps.release.outputs.upload_url }} + GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} From 4339847da55fa232f22f2f81c77aff4119ed7180 Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Fri, 16 Dec 2022 23:21:07 +0100 Subject: [PATCH 05/12] fix yuzu build Accidentially moved it into the wrong directory. --- .github/actions/build/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 1524ee4..ec64178 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -70,7 +70,7 @@ runs: mkdir ./SMOO/ mv ./atmosphere/contents/0100000000010000/exefs ./SMOO/exefs mv ./atmosphere/contents/0100000000010000/romfs ./SMOO/romfs - mv ./atmosphere/exefs_patches/StarlightBase/3CA12DFAAF9C82DA064D1698DF79CDA1.ips ./SMOO/romfs/ + mv ./atmosphere/exefs_patches/StarlightBase/3CA12DFAAF9C82DA064D1698DF79CDA1.ips ./SMOO/exefs/ rm -rf ./atmosphere/ - name : Upload artifacts From 27b9a095f7b6bd8d1c02eef80afee35ab53f7d94 Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Fri, 23 Jun 2023 04:18:41 +0200 Subject: [PATCH 06/12] fix: mark player as not a seeker when H&S is disabled If a player is a seeker and then disables H&S, they should no longer be considered as a seeker by other players. (Because staying as a seeker will kill hiders when touching them). --- source/server/Client.cpp | 2 +- source/server/hns/HideAndSeekMode.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/source/server/Client.cpp b/source/server/Client.cpp index 4457ef0..c047fdd 100644 --- a/source/server/Client.cpp +++ b/source/server/Client.cpp @@ -622,7 +622,7 @@ void Client::sendTagInfPacket() { packet->mUserID = sInstance->mUserID; - packet->isIt = hsMode->isPlayerIt(); + packet->isIt = hsMode->isPlayerIt() && hsMode->isModeActive(); packet->minutes = curInfo->mHidingTime.mMinutes; packet->seconds = curInfo->mHidingTime.mSeconds; diff --git a/source/server/hns/HideAndSeekMode.cpp b/source/server/hns/HideAndSeekMode.cpp index 65e97c0..9efbdaf 100644 --- a/source/server/hns/HideAndSeekMode.cpp +++ b/source/server/hns/HideAndSeekMode.cpp @@ -84,6 +84,8 @@ void HideAndSeekMode::begin() { playGuideLyt->end(); GameModeBase::begin(); + + Client::sendTagInfPacket(); } void HideAndSeekMode::end() { @@ -109,6 +111,8 @@ void HideAndSeekMode::end() { playGuideLyt->appear(); GameModeBase::end(); + + Client::sendTagInfPacket(); } void HideAndSeekMode::update() { From 48356926727033f5b8bcb4556b5e35614c55f6f2 Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Sat, 24 Jun 2023 01:27:14 +0200 Subject: [PATCH 07/12] fix: resend TagInf packet on reconnect and for new players --- include/server/Client.hpp | 1 + source/server/Client.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/server/Client.hpp b/include/server/Client.hpp index 028fee9..5a0b4a3 100644 --- a/include/server/Client.hpp +++ b/include/server/Client.hpp @@ -229,6 +229,7 @@ class Client { GameInf lastGameInfPacket = GameInf(); GameInf emptyGameInfPacket = GameInf(); CostumeInf lastCostumeInfPacket = CostumeInf(); + TagInf lastTagInfPacket = TagInf(); Keyboard* mKeyboard = nullptr; // keyboard for setting server IP diff --git a/source/server/Client.cpp b/source/server/Client.cpp index 4457ef0..cb59fe2 100644 --- a/source/server/Client.cpp +++ b/source/server/Client.cpp @@ -361,6 +361,8 @@ void Client::readFunc() { mSocket->send(&lastPlayerInfPacket); if (lastCostumeInfPacket.mUserID == mUserID) mSocket->send(&lastCostumeInfPacket); + if (lastTagInfPacket.mUserID == mUserID) + mSocket->send(&lastTagInfPacket); break; case PacketType::COSTUMEINF: @@ -629,6 +631,8 @@ void Client::sendTagInfPacket() { packet->updateType = static_cast(TagUpdateType::STATE | TagUpdateType::TIME); sInstance->mSocket->queuePacket(packet); + + sInstance->lastTagInfPacket = *packet; } /** @@ -696,6 +700,11 @@ void Client::resendInitPackets() { if (lastGameInfPacket != emptyGameInfPacket) { mSocket->queuePacket(&lastGameInfPacket); } + + // TagInfPacket + if (lastTagInfPacket.mUserID == mUserID) { + mSocket->queuePacket(&lastTagInfPacket); + } } /** From 2bef6f796ec4c9dd6f50e66787d1972f9e78a12b Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Sat, 24 Jun 2023 02:42:54 +0200 Subject: [PATCH 08/12] fix: resend CaptureInf packet on reconnect and for new players --- include/server/Client.hpp | 1 + source/server/Client.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/server/Client.hpp b/include/server/Client.hpp index 5a0b4a3..4ced2aa 100644 --- a/include/server/Client.hpp +++ b/include/server/Client.hpp @@ -230,6 +230,7 @@ class Client { GameInf emptyGameInfPacket = GameInf(); CostumeInf lastCostumeInfPacket = CostumeInf(); TagInf lastTagInfPacket = TagInf(); + CaptureInf lastCaptureInfPacket = CaptureInf(); Keyboard* mKeyboard = nullptr; // keyboard for setting server IP diff --git a/source/server/Client.cpp b/source/server/Client.cpp index cb59fe2..ccc81b2 100644 --- a/source/server/Client.cpp +++ b/source/server/Client.cpp @@ -363,6 +363,8 @@ void Client::readFunc() { mSocket->send(&lastCostumeInfPacket); if (lastTagInfPacket.mUserID == mUserID) mSocket->send(&lastTagInfPacket); + if (lastCaptureInfPacket.mUserID == mUserID) + mSocket->send(&lastCaptureInfPacket); break; case PacketType::COSTUMEINF: @@ -677,12 +679,14 @@ void Client::sendCaptureInfPacket(const PlayerActorHakoniwa* player) { packet->mUserID = sInstance->mUserID; strcpy(packet->hackName, tryConvertName(player->mHackKeeper->getCurrentHackName())); sInstance->mSocket->queuePacket(packet); + sInstance->lastCaptureInfPacket = *packet; sInstance->isSentCaptureInf = true; } else if (!sInstance->isClientCaptured && sInstance->isSentCaptureInf) { CaptureInf *packet = new CaptureInf(); packet->mUserID = sInstance->mUserID; strcpy(packet->hackName, ""); sInstance->mSocket->queuePacket(packet); + sInstance->lastCaptureInfPacket = *packet; sInstance->isSentCaptureInf = false; } } @@ -705,6 +709,11 @@ void Client::resendInitPackets() { if (lastTagInfPacket.mUserID == mUserID) { mSocket->queuePacket(&lastTagInfPacket); } + + // CaptureInfPacket + if (lastCaptureInfPacket.mUserID == mUserID) { + mSocket->queuePacket(&lastCaptureInfPacket); + } } /** From f810e8f07d742bb5ca4a50113ac471c60d898620 Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Sat, 24 Jun 2023 04:09:47 +0200 Subject: [PATCH 09/12] fix: send empty TagInf and CaptureInf on first connection Because other players might still have old values saved in their puppet. --- source/server/SocketClient.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/source/server/SocketClient.cpp b/source/server/SocketClient.cpp index 2389cc7..9495df2 100644 --- a/source/server/SocketClient.cpp +++ b/source/server/SocketClient.cpp @@ -104,6 +104,21 @@ nn::Result SocketClient::init(const char* ip, u16 port) { // on a reconnect, resend some maybe missing packets if (initPacket.conType == ConnectionTypes::RECONNECT) { client->resendInitPackets(); + } else { + // empty TagInf + TagInf tagInf; + tagInf.mUserID = initPacket.mUserID; + tagInf.isIt = false; + tagInf.minutes = 0; + tagInf.seconds = 0; + tagInf.updateType = static_cast(TagUpdateType::STATE | TagUpdateType::TIME); + send(&tagInf); + + // empty CaptureInf + CaptureInf capInf; + capInf.mUserID = initPacket.mUserID; + strcpy(capInf.hackName, ""); + send(&capInf); } return result; From 366ad5e8886516d21bd7df7145e345190f6e8074 Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Fri, 7 Jul 2023 23:30:10 +0200 Subject: [PATCH 10/12] remove reconnect button --- .../StageSceneStateServerConfig.hpp | 9 +-- include/server/Client.hpp | 10 --- source/server/Client.cpp | 77 ------------------- source/states/StageSceneStateServerConfig.cpp | 48 +----------- 4 files changed, 6 insertions(+), 138 deletions(-) diff --git a/include/game/StageScene/StageSceneStateServerConfig.hpp b/include/game/StageScene/StageSceneStateServerConfig.hpp index 8956bdc..9f9c245 100644 --- a/include/game/StageScene/StageSceneStateServerConfig.hpp +++ b/include/game/StageScene/StageSceneStateServerConfig.hpp @@ -29,7 +29,6 @@ class StageSceneStateServerConfig : public al::HostStateBase, public enum ServerConfigOption { GAMEMODECONFIG, GAMEMODESWITCH, - RECONNECT, SETIP, SETPORT }; @@ -42,11 +41,9 @@ class StageSceneStateServerConfig : public al::HostStateBase, public void exeMainMenu(); void exeOpenKeyboardIP(); void exeOpenKeyboardPort(); - void exeRestartServer(); void exeGamemodeConfig(); void exeGamemodeSelect(); void exeSaveData(); - void exeConnectError(); void endSubMenu(); @@ -63,7 +60,7 @@ class StageSceneStateServerConfig : public al::HostStateBase, public SimpleLayoutMenu* mCurrentMenu = nullptr; CommonVerticalList* mCurrentList = nullptr; - // Root Page, contains buttons for gamemode config, server reconnecting, and server ip address changing + // Root Page, contains buttons for gamemode config, and server ip address changing SimpleLayoutMenu* mMainOptions = nullptr; CommonVerticalList *mMainOptionsList = nullptr; // Sub-Page of Mode config, used to select a gamemode for the client to use @@ -86,9 +83,7 @@ namespace { NERVE_HEADER(StageSceneStateServerConfig, MainMenu) NERVE_HEADER(StageSceneStateServerConfig, OpenKeyboardIP) NERVE_HEADER(StageSceneStateServerConfig, OpenKeyboardPort) - NERVE_HEADER(StageSceneStateServerConfig, RestartServer) NERVE_HEADER(StageSceneStateServerConfig, GamemodeConfig) NERVE_HEADER(StageSceneStateServerConfig, GamemodeSelect) NERVE_HEADER(StageSceneStateServerConfig, SaveData) - NERVE_HEADER(StageSceneStateServerConfig, ConnectError) -} \ No newline at end of file +} diff --git a/include/server/Client.hpp b/include/server/Client.hpp index 028fee9..fb63593 100644 --- a/include/server/Client.hpp +++ b/include/server/Client.hpp @@ -16,7 +16,6 @@ #include "al/LiveActor/LiveActor.h" #include "al/layout/LayoutInitInfo.h" #include "al/layout/SimpleLayoutAppearWaitEnd.h" -#include "al/layout/WindowConfirmWait.h" #include "al/util.hpp" #include "al/layout/LayoutActor.h" #include "al/gamepad/util.h" @@ -88,7 +87,6 @@ class Client { bool startThread(); void readFunc(); - static void restartConnection(); static bool isSocketActive() { return sInstance ? sInstance->mSocket->isConnected() : false; }; bool isPlayerConnected(int index) { return mPuppetInfoArr[index]->isConnected; } @@ -175,12 +173,6 @@ class Client { static bool openKeyboardIP(); static bool openKeyboardPort(); - static void showConnect(); - - static void showConnectError(const char16_t* msg); - - static void hideConnect(); - void resetCollectedShines(); void removeShine(int shineId); @@ -241,8 +233,6 @@ class Client { // --- Game Layouts --- - al::WindowConfirmWait* mConnectionWait; - al::SimpleLayoutAppearWaitEnd *mConnectStatus; // --- Game Info --- diff --git a/source/server/Client.cpp b/source/server/Client.cpp index 4457ef0..a53fcda 100644 --- a/source/server/Client.cpp +++ b/source/server/Client.cpp @@ -71,13 +71,8 @@ Client::Client() { */ void Client::init(al::LayoutInitInfo const &initInfo, GameDataHolderAccessor holder) { - mConnectionWait = new (mHeap) al::WindowConfirmWait("ServerWaitConnect", "WindowConfirmWait", initInfo); - mConnectStatus = new (mHeap) al::SimpleLayoutAppearWaitEnd("", "SaveMessage", initInfo, 0, false); - mConnectionWait->setTxtMessage(u"Connecting to Server."); - mConnectionWait->setTxtMessageConfirm(u"Failed to Connect!"); - al::setPaneString(mConnectStatus, "TxtSave", u"Connecting to Server.", 0); al::setPaneString(mConnectStatus, "TxtSaveSh", u"Connecting to Server.", 0); @@ -105,44 +100,6 @@ bool Client::startThread() { } } -/** - * @brief restarts currently active connection to server - * - */ -void Client::restartConnection() { - - if (!sInstance) { - Logger::log("Static Instance is null!\n"); - return; - } - - sead::ScopedCurrentHeapSetter setter(sInstance->mHeap); - - Logger::log("Sending Disconnect.\n"); - - PlayerDC *playerDC = new PlayerDC(); - - playerDC->mUserID = sInstance->mUserID; - - sInstance->mSocket->queuePacket(playerDC); - - if (sInstance->mSocket->closeSocket()) { - Logger::log("Sucessfully Closed Socket.\n"); - } - - sInstance->mConnectCount = 0; - - sInstance->mIsConnectionActive = sInstance->mSocket->init(sInstance->mServerIP.cstr(), sInstance->mServerPort).isSuccess(); - - if(sInstance->mSocket->getLogState() == SOCKET_LOG_CONNECTED) { - - Logger::log("Reconnect Sucessful!\n"); - - } else { - Logger::log("Reconnect Unsuccessful.\n"); - } -} - /** * @brief starts a connection using client's TCP socket class, pulling up the software keyboard for user inputted IP if save file does not have one saved. * @@ -1393,37 +1350,3 @@ Shine* Client::findStageShine(int shineID) { } return nullptr; } - -void Client::showConnectError(const char16_t* msg) { - if (!sInstance) - return; - - sInstance->mConnectionWait->setTxtMessageConfirm(msg); - - al::hidePane(sInstance->mConnectionWait, "Page01"); // hide A button prompt - - if (!sInstance->mConnectionWait->mIsAlive) { - sInstance->mConnectionWait->appear(); - - sInstance->mConnectionWait->playLoop(); - } - - al::startAction(sInstance->mConnectionWait, "Confirm", "State"); -} - -void Client::showConnect() { - if (!sInstance) - return; - - sInstance->mConnectionWait->appear(); - - sInstance->mConnectionWait->playLoop(); - -} - -void Client::hideConnect() { - if (!sInstance) - return; - - sInstance->mConnectionWait->tryEnd(); -} diff --git a/source/states/StageSceneStateServerConfig.cpp b/source/states/StageSceneStateServerConfig.cpp index e8d0e2e..34c478a 100644 --- a/source/states/StageSceneStateServerConfig.cpp +++ b/source/states/StageSceneStateServerConfig.cpp @@ -41,14 +41,13 @@ StageSceneStateServerConfig::StageSceneStateServerConfig(const char *name, al::S mMainOptionsList->unkInt1 = 1; - mMainOptionsList->initDataNoResetSelected(5); + mMainOptionsList->initDataNoResetSelected(4); - sead::SafeArray, 5>* mainMenuOptions = - new sead::SafeArray, 5>(); + sead::SafeArray, 4>* mainMenuOptions = + new sead::SafeArray, 4>(); mainMenuOptions->mBuffer[ServerConfigOption::GAMEMODECONFIG].copy(u"Gamemode Config"); mainMenuOptions->mBuffer[ServerConfigOption::GAMEMODESWITCH].copy(u"Change Gamemode"); - mainMenuOptions->mBuffer[ServerConfigOption::RECONNECT].copy(u"Reconnect to Server"); mainMenuOptions->mBuffer[ServerConfigOption::SETIP].copy(u"Change Server IP"); mainMenuOptions->mBuffer[ServerConfigOption::SETPORT].copy(u"Change Server Port"); @@ -176,10 +175,6 @@ void StageSceneStateServerConfig::exeMainMenu() { al::setNerve(this, &nrvStageSceneStateServerConfigGamemodeSelect); break; } - case ServerConfigOption::RECONNECT: { - al::setNerve(this, &nrvStageSceneStateServerConfigRestartServer); - break; - } case ServerConfigOption::SETIP: { al::setNerve(this, &nrvStageSceneStateServerConfigOpenKeyboardIP); break; @@ -233,27 +228,6 @@ void StageSceneStateServerConfig::exeOpenKeyboardPort() { } } -void StageSceneStateServerConfig::exeRestartServer() { - if (al::isFirstStep(this)) { - mCurrentList->deactivate(); - - Client::showConnect(); - - Client::restartConnection(); - } - - if (Client::isSocketActive()) { - - Client::hideConnect(); - - al::startHitReaction(mCurrentMenu, "リセット", 0); - - al::setNerve(this, &nrvStageSceneStateServerConfigMainMenu); - } else { - al::setNerve(this, &nrvStageSceneStateServerConfigConnectError); - } -} - void StageSceneStateServerConfig::exeGamemodeConfig() { if (al::isFirstStep(this)) { mGamemodeConfigMenu = &mGamemodeConfigMenus[GameModeManager::instance()->getGameMode()]; @@ -290,18 +264,6 @@ void StageSceneStateServerConfig::exeGamemodeSelect() { } } -void StageSceneStateServerConfig::exeConnectError() { - if (al::isFirstStep(this)) { - Client::showConnectError(u"Failed to Reconnect!"); - } - - if (al::isGreaterEqualStep(this, 60)) { // close after 1 second - Client::hideConnect(); - al::startHitReaction(mCurrentMenu, "リセット", 0); - al::setNerve(this, &nrvStageSceneStateServerConfigMainMenu); - } -} - void StageSceneStateServerConfig::exeSaveData() { if (al::isFirstStep(this)) { @@ -373,9 +335,7 @@ namespace { NERVE_IMPL(StageSceneStateServerConfig, MainMenu) NERVE_IMPL(StageSceneStateServerConfig, OpenKeyboardIP) NERVE_IMPL(StageSceneStateServerConfig, OpenKeyboardPort) -NERVE_IMPL(StageSceneStateServerConfig, RestartServer) NERVE_IMPL(StageSceneStateServerConfig, GamemodeConfig) NERVE_IMPL(StageSceneStateServerConfig, GamemodeSelect) NERVE_IMPL(StageSceneStateServerConfig, SaveData) -NERVE_IMPL(StageSceneStateServerConfig, ConnectError) -} \ No newline at end of file +} From c736878df1515eabc2ac0844dd0b3f04e8283107 Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Sat, 8 Jul 2023 04:31:56 +0200 Subject: [PATCH 11/12] `Toggle H&S Gravity` with real toggle functionality --- include/server/hns/HideAndSeekConfigMenu.hpp | 8 +++-- source/server/hns/HideAndSeekConfigMenu.cpp | 35 ++++++++++--------- source/states/StageSceneStateServerConfig.cpp | 10 +++--- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/include/server/hns/HideAndSeekConfigMenu.hpp b/include/server/hns/HideAndSeekConfigMenu.hpp index f2039c9..3564912 100644 --- a/include/server/hns/HideAndSeekConfigMenu.hpp +++ b/include/server/hns/HideAndSeekConfigMenu.hpp @@ -9,11 +9,13 @@ public: HideAndSeekConfigMenu(); void initMenu(const al::LayoutInitInfo &initInfo) override; - const sead::WFixedSafeString<0x200> *getStringData() override; + const sead::WFixedSafeString<0x200>* getStringData() override; bool updateMenu(int selectIndex) override; const int getMenuSize() override { return mItemCount; } private: - static constexpr int mItemCount = 2; -}; \ No newline at end of file + static constexpr int mItemCount = 1; + sead::SafeArray, mItemCount>* gravityOn = nullptr; + sead::SafeArray, mItemCount>* gravityOff = nullptr; +}; diff --git a/source/server/hns/HideAndSeekConfigMenu.cpp b/source/server/hns/HideAndSeekConfigMenu.cpp index 79fc7a4..c34e786 100644 --- a/source/server/hns/HideAndSeekConfigMenu.cpp +++ b/source/server/hns/HideAndSeekConfigMenu.cpp @@ -5,20 +5,27 @@ #include "server/hns/HideAndSeekMode.hpp" #include "server/Client.hpp" -HideAndSeekConfigMenu::HideAndSeekConfigMenu() : GameModeConfigMenu() {} +HideAndSeekConfigMenu::HideAndSeekConfigMenu() : GameModeConfigMenu() { + gravityOn = new sead::SafeArray, mItemCount>(); + gravityOn->mBuffer[0].copy(u"Toggle H&S Gravity (ON)"); + + gravityOff = new sead::SafeArray, mItemCount>(); + gravityOff->mBuffer[0].copy(u"Toggle H&S Gravity (OFF)"); +} void HideAndSeekConfigMenu::initMenu(const al::LayoutInitInfo &initInfo) { } -const sead::WFixedSafeString<0x200> *HideAndSeekConfigMenu::getStringData() { - sead::SafeArray, mItemCount>* gamemodeConfigOptions = - new sead::SafeArray, mItemCount>(); - - gamemodeConfigOptions->mBuffer[0].copy(u"Toggle H&S Gravity On"); - gamemodeConfigOptions->mBuffer[1].copy(u"Toggle H&S Gravity Off"); - - return gamemodeConfigOptions->mBuffer; +const sead::WFixedSafeString<0x200>* HideAndSeekConfigMenu::getStringData() { + HideAndSeekInfo *curMode = GameModeManager::instance()->getInfo(); + return ( + GameModeManager::instance()->isMode(GameMode::HIDEANDSEEK) + && curMode != nullptr + && curMode->mIsUseGravity + ? gravityOn->mBuffer + : gravityOff->mBuffer + ); } bool HideAndSeekConfigMenu::updateMenu(int selectIndex) { @@ -35,13 +42,7 @@ bool HideAndSeekConfigMenu::updateMenu(int selectIndex) { switch (selectIndex) { case 0: { if (GameModeManager::instance()->isMode(GameMode::HIDEANDSEEK)) { - curMode->mIsUseGravity = true; - } - return true; - } - case 1: { - if (GameModeManager::instance()->isMode(GameMode::HIDEANDSEEK)) { - curMode->mIsUseGravity = false; + curMode->mIsUseGravity = !curMode->mIsUseGravity; } return true; } @@ -50,4 +51,4 @@ bool HideAndSeekConfigMenu::updateMenu(int selectIndex) { return false; } -} \ No newline at end of file +} diff --git a/source/states/StageSceneStateServerConfig.cpp b/source/states/StageSceneStateServerConfig.cpp index 34c478a..4ec5bfc 100644 --- a/source/states/StageSceneStateServerConfig.cpp +++ b/source/states/StageSceneStateServerConfig.cpp @@ -100,11 +100,6 @@ StageSceneStateServerConfig::StageSceneStateServerConfig(const char *name, al::S entry.mList = new CommonVerticalList(entry.mLayout, initInfo, true); al::setPaneString(entry.mLayout, "TxtOption", u"Gamemode Configuration", 0); - - entry.mList->initDataNoResetSelected(entry.mMenu->getMenuSize()); - - - entry.mList->addStringData(entry.mMenu->getStringData(), "TxtContent"); } @@ -231,8 +226,13 @@ void StageSceneStateServerConfig::exeOpenKeyboardPort() { void StageSceneStateServerConfig::exeGamemodeConfig() { if (al::isFirstStep(this)) { mGamemodeConfigMenu = &mGamemodeConfigMenus[GameModeManager::instance()->getGameMode()]; + + mGamemodeConfigMenu->mList->initDataNoResetSelected(mGamemodeConfigMenu->mMenu->getMenuSize()); + mGamemodeConfigMenu->mList->addStringData(mGamemodeConfigMenu->mMenu->getStringData(), "TxtContent"); + mCurrentList = mGamemodeConfigMenu->mList; mCurrentMenu = mGamemodeConfigMenu->mLayout; + subMenuStart(); } From 45cd4442c87f12898829e8717a5d234219345a82 Mon Sep 17 00:00:00 2001 From: "Robin C. Ladiges" Date: Sat, 8 Jul 2023 20:45:53 +0200 Subject: [PATCH 12/12] recycle the reconnect error to warn about the default Ryujinx profile ID --- include/server/Client.hpp | 6 +++- source/server/Client.cpp | 32 ++++++++++++++++++- source/states/StageSceneStateServerConfig.cpp | 9 ++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/include/server/Client.hpp b/include/server/Client.hpp index fb63593..7d3d898 100644 --- a/include/server/Client.hpp +++ b/include/server/Client.hpp @@ -16,6 +16,7 @@ #include "al/LiveActor/LiveActor.h" #include "al/layout/LayoutInitInfo.h" #include "al/layout/SimpleLayoutAppearWaitEnd.h" +#include "al/layout/WindowConfirmWait.h" #include "al/util.hpp" #include "al/layout/LayoutActor.h" #include "al/gamepad/util.h" @@ -173,6 +174,9 @@ class Client { static bool openKeyboardIP(); static bool openKeyboardPort(); + static void showUIMessage(const char16_t* msg); + static void hideUIMessage(); + void resetCollectedShines(); void removeShine(int shineId); @@ -232,7 +236,7 @@ class Client { bool mIsFirstConnect = true; // --- Game Layouts --- - + al::WindowConfirmWait* mUIMessage; al::SimpleLayoutAppearWaitEnd *mConnectStatus; // --- Game Info --- diff --git a/source/server/Client.cpp b/source/server/Client.cpp index a53fcda..207adcf 100644 --- a/source/server/Client.cpp +++ b/source/server/Client.cpp @@ -72,10 +72,13 @@ Client::Client() { void Client::init(al::LayoutInitInfo const &initInfo, GameDataHolderAccessor holder) { mConnectStatus = new (mHeap) al::SimpleLayoutAppearWaitEnd("", "SaveMessage", initInfo, 0, false); - al::setPaneString(mConnectStatus, "TxtSave", u"Connecting to Server.", 0); al::setPaneString(mConnectStatus, "TxtSaveSh", u"Connecting to Server.", 0); + mUIMessage = new (mHeap) al::WindowConfirmWait("ServerWaitConnect", "WindowConfirmWait", initInfo); + mUIMessage->setTxtMessage(u"a"); + mUIMessage->setTxtMessageConfirm(u"b"); + mHolder = holder; startThread(); @@ -250,6 +253,33 @@ bool Client::openKeyboardPort() { return isFirstConnect; } + +void Client::showUIMessage(const char16_t* msg) { + if (!sInstance) { + return; + } + + sInstance->mUIMessage->setTxtMessageConfirm(msg); + + al::hidePane(sInstance->mUIMessage, "Page01"); // hide A button prompt + + if (!sInstance->mUIMessage->mIsAlive) { + sInstance->mUIMessage->appear(); + + sInstance->mUIMessage->playLoop(); + } + + al::startAction(sInstance->mUIMessage, "Confirm", "State"); +} + +void Client::hideUIMessage() { + if (!sInstance) { + return; + } + + sInstance->mUIMessage->tryEnd(); +} + /** * @brief main thread function for read thread, responsible for processing packets from server * diff --git a/source/states/StageSceneStateServerConfig.cpp b/source/states/StageSceneStateServerConfig.cpp index 4ec5bfc..3d6a096 100644 --- a/source/states/StageSceneStateServerConfig.cpp +++ b/source/states/StageSceneStateServerConfig.cpp @@ -109,6 +109,15 @@ StageSceneStateServerConfig::StageSceneStateServerConfig(const char *name, al::S void StageSceneStateServerConfig::init() { initNerve(&nrvStageSceneStateServerConfigMainMenu, 0); + + #ifdef EMU + char ryujinx[0x10] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + nn::account::Uid user; + nn::account::GetLastOpenedUser(&user); + if (memcmp(user.data, ryujinx, 0x10) == 0) { + Client::showUIMessage(u"Error: Ryujinx default profile detected.\nYou have to create a new user profile!"); + } + #endif } void StageSceneStateServerConfig::appear(void) {