mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-22 10:19:01 +00:00
Merge pull request #65 from bmarcott/nmarcott_format
apply clang-format to all .cpp and .h files containing the word slippi
This commit is contained in:
commit
a557edb283
21 changed files with 1616 additions and 1419 deletions
1088
Externals/SlippiLib/SlippiGame.cpp
vendored
1088
Externals/SlippiLib/SlippiGame.cpp
vendored
File diff suppressed because it is too large
Load diff
248
Externals/SlippiLib/SlippiGame.h
vendored
248
Externals/SlippiLib/SlippiGame.h
vendored
|
@ -1,143 +1,151 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Slippi {
|
namespace Slippi
|
||||||
const uint8_t EVENT_SPLIT_MESSAGE = 0x10;
|
{
|
||||||
const uint8_t EVENT_PAYLOAD_SIZES = 0x35;
|
const uint8_t EVENT_SPLIT_MESSAGE = 0x10;
|
||||||
const uint8_t EVENT_GAME_INIT = 0x36;
|
const uint8_t EVENT_PAYLOAD_SIZES = 0x35;
|
||||||
const uint8_t EVENT_PRE_FRAME_UPDATE = 0x37;
|
const uint8_t EVENT_GAME_INIT = 0x36;
|
||||||
const uint8_t EVENT_POST_FRAME_UPDATE = 0x38;
|
const uint8_t EVENT_PRE_FRAME_UPDATE = 0x37;
|
||||||
const uint8_t EVENT_GAME_END = 0x39;
|
const uint8_t EVENT_POST_FRAME_UPDATE = 0x38;
|
||||||
const uint8_t EVENT_FRAME_START = 0x3A;
|
const uint8_t EVENT_GAME_END = 0x39;
|
||||||
const uint8_t EVENT_GECKO_LIST = 0x3D;
|
const uint8_t EVENT_FRAME_START = 0x3A;
|
||||||
|
const uint8_t EVENT_GECKO_LIST = 0x3D;
|
||||||
|
|
||||||
const uint8_t GAME_INFO_HEADER_SIZE = 78;
|
const uint8_t GAME_INFO_HEADER_SIZE = 78;
|
||||||
const uint8_t UCF_TOGGLE_SIZE = 8;
|
const uint8_t UCF_TOGGLE_SIZE = 8;
|
||||||
const uint8_t NAMETAG_SIZE = 8;
|
const uint8_t NAMETAG_SIZE = 8;
|
||||||
const int32_t GAME_FIRST_FRAME = -123;
|
const int32_t GAME_FIRST_FRAME = -123;
|
||||||
const int32_t PLAYBACK_FIRST_SAVE = -122;
|
const int32_t PLAYBACK_FIRST_SAVE = -122;
|
||||||
const uint8_t GAME_SHEIK_INTERNAL_ID = 0x7;
|
const uint8_t GAME_SHEIK_INTERNAL_ID = 0x7;
|
||||||
const uint8_t GAME_SHEIK_EXTERNAL_ID = 0x13;
|
const uint8_t GAME_SHEIK_EXTERNAL_ID = 0x13;
|
||||||
|
|
||||||
const uint32_t SPLIT_MESSAGE_INTERNAL_DATA_LEN = 512;
|
const uint32_t SPLIT_MESSAGE_INTERNAL_DATA_LEN = 512;
|
||||||
|
|
||||||
static uint8_t* data;
|
static uint8_t* data;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
// Every player update has its own rng seed because it might change in between players
|
{
|
||||||
uint32_t randomSeed;
|
// Every player update has its own rng seed because it might change in between
|
||||||
|
// players
|
||||||
|
uint32_t randomSeed;
|
||||||
|
|
||||||
uint8_t internalCharacterId;
|
uint8_t internalCharacterId;
|
||||||
uint16_t animation;
|
uint16_t animation;
|
||||||
float locationX;
|
float locationX;
|
||||||
float locationY;
|
float locationY;
|
||||||
float facingDirection;
|
float facingDirection;
|
||||||
uint8_t stocks;
|
uint8_t stocks;
|
||||||
float percent;
|
float percent;
|
||||||
float shieldSize;
|
float shieldSize;
|
||||||
uint8_t lastMoveHitId;
|
uint8_t lastMoveHitId;
|
||||||
uint8_t comboCount;
|
uint8_t comboCount;
|
||||||
uint8_t lastHitBy;
|
uint8_t lastHitBy;
|
||||||
|
|
||||||
//Controller information
|
// Controller information
|
||||||
float joystickX;
|
float joystickX;
|
||||||
float joystickY;
|
float joystickY;
|
||||||
float cstickX;
|
float cstickX;
|
||||||
float cstickY;
|
float cstickY;
|
||||||
float trigger;
|
float trigger;
|
||||||
uint32_t buttons; //This will include multiple "buttons" pressed on special buttons. For example I think pressing z sets 3 bits
|
uint32_t buttons; // This will include multiple "buttons" pressed on special
|
||||||
|
// buttons. For example I think pressing z sets 3 bits
|
||||||
|
|
||||||
//This is extra controller information
|
// This is extra controller information
|
||||||
uint16_t physicalButtons; //A better representation of what a player is actually pressing
|
uint16_t physicalButtons; // A better representation of what a player is
|
||||||
float lTrigger;
|
// actually pressing
|
||||||
float rTrigger;
|
float lTrigger;
|
||||||
|
float rTrigger;
|
||||||
|
|
||||||
uint8_t joystickXRaw;
|
uint8_t joystickXRaw;
|
||||||
} PlayerFrameData;
|
} PlayerFrameData;
|
||||||
|
|
||||||
typedef struct FrameData {
|
typedef struct FrameData
|
||||||
int32_t frame;
|
{
|
||||||
uint32_t numSinceStart;
|
int32_t frame;
|
||||||
bool randomSeedExists = false;
|
uint32_t numSinceStart;
|
||||||
uint32_t randomSeed;
|
bool randomSeedExists = false;
|
||||||
bool inputsFullyFetched = false;
|
uint32_t randomSeed;
|
||||||
std::unordered_map<uint8_t, PlayerFrameData> players;
|
bool inputsFullyFetched = false;
|
||||||
std::unordered_map<uint8_t, PlayerFrameData> followers;
|
std::unordered_map<uint8_t, PlayerFrameData> players;
|
||||||
} FrameData;
|
std::unordered_map<uint8_t, PlayerFrameData> followers;
|
||||||
|
} FrameData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
//Static data
|
{
|
||||||
uint8_t characterId;
|
// Static data
|
||||||
uint8_t characterColor;
|
uint8_t characterId;
|
||||||
uint8_t playerType;
|
uint8_t characterColor;
|
||||||
uint8_t controllerPort;
|
uint8_t playerType;
|
||||||
std::array<uint16_t, NAMETAG_SIZE> nametag;
|
uint8_t controllerPort;
|
||||||
} PlayerSettings;
|
std::array<uint16_t, NAMETAG_SIZE> nametag;
|
||||||
|
} PlayerSettings;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
uint16_t stage; //Stage ID
|
{
|
||||||
uint32_t randomSeed;
|
uint16_t stage; // Stage ID
|
||||||
std::array<uint32_t, GAME_INFO_HEADER_SIZE> header;
|
uint32_t randomSeed;
|
||||||
std::array<uint32_t, UCF_TOGGLE_SIZE> ucfToggles;
|
std::array<uint32_t, GAME_INFO_HEADER_SIZE> header;
|
||||||
std::unordered_map<uint8_t, PlayerSettings> players;
|
std::array<uint32_t, UCF_TOGGLE_SIZE> ucfToggles;
|
||||||
uint8_t isPAL;
|
std::unordered_map<uint8_t, PlayerSettings> players;
|
||||||
uint8_t isFrozenPS;
|
uint8_t isPAL;
|
||||||
std::vector<uint8_t> geckoCodes;
|
uint8_t isFrozenPS;
|
||||||
} GameSettings;
|
std::vector<uint8_t> geckoCodes;
|
||||||
|
} GameSettings;
|
||||||
|
|
||||||
typedef struct Game {
|
typedef struct Game
|
||||||
std::array<uint8_t, 4> version;
|
{
|
||||||
std::unordered_map<int32_t, FrameData*> framesByIndex;
|
std::array<uint8_t, 4> version;
|
||||||
std::vector<std::unique_ptr<FrameData>> frames;
|
std::unordered_map<int32_t, FrameData*> framesByIndex;
|
||||||
GameSettings settings;
|
std::vector<std::unique_ptr<FrameData>> frames;
|
||||||
bool areSettingsLoaded = false;
|
GameSettings settings;
|
||||||
|
bool areSettingsLoaded = false;
|
||||||
|
|
||||||
int32_t frameCount; // Current/last frame count
|
int32_t frameCount; // Current/last frame count
|
||||||
|
|
||||||
//From OnGameEnd event
|
// From OnGameEnd event
|
||||||
uint8_t winCondition;
|
uint8_t winCondition;
|
||||||
} Game;
|
} Game;
|
||||||
|
|
||||||
// TODO: This shouldn't be static. Doesn't matter too much atm because we always
|
// TODO: This shouldn't be static. Doesn't matter too much atm because we always
|
||||||
// TODO: only read one file at a time
|
// TODO: only read one file at a time
|
||||||
static std::unordered_map<uint8_t, uint32_t> asmEvents = {
|
static std::unordered_map<uint8_t, uint32_t> asmEvents = {{EVENT_GAME_INIT, 320},
|
||||||
{ EVENT_GAME_INIT, 320 },
|
{EVENT_PRE_FRAME_UPDATE, 58},
|
||||||
{ EVENT_PRE_FRAME_UPDATE, 58 },
|
{EVENT_POST_FRAME_UPDATE, 33},
|
||||||
{ EVENT_POST_FRAME_UPDATE, 33 },
|
{EVENT_GAME_END, 1},
|
||||||
{ EVENT_GAME_END, 1 },
|
{EVENT_FRAME_START, 8}};
|
||||||
{ EVENT_FRAME_START, 8 }
|
|
||||||
};
|
|
||||||
|
|
||||||
class SlippiGame
|
class SlippiGame
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::unique_ptr<SlippiGame> FromFile(std::string path);
|
static std::unique_ptr<SlippiGame> FromFile(std::string path);
|
||||||
bool AreSettingsLoaded();
|
bool AreSettingsLoaded();
|
||||||
bool DoesFrameExist(int32_t frame);
|
bool DoesFrameExist(int32_t frame);
|
||||||
std::array<uint8_t, 4> GetVersion();
|
std::array<uint8_t, 4> GetVersion();
|
||||||
FrameData* GetFrame(int32_t frame);
|
FrameData* GetFrame(int32_t frame);
|
||||||
FrameData* GetFrameAt(uint32_t pos);
|
FrameData* GetFrameAt(uint32_t pos);
|
||||||
int32_t GetLatestIndex();
|
int32_t GetLatestIndex();
|
||||||
GameSettings* GetSettings();
|
GameSettings* GetSettings();
|
||||||
bool DoesPlayerExist(int8_t port);
|
bool DoesPlayerExist(int8_t port);
|
||||||
bool IsProcessingComplete();
|
bool IsProcessingComplete();
|
||||||
private:
|
|
||||||
std::unique_ptr<Game> game;
|
|
||||||
std::unique_ptr<std::ifstream> file;
|
|
||||||
std::vector<uint8_t> rawData;
|
|
||||||
std::string path;
|
|
||||||
std::ofstream log;
|
|
||||||
std::vector<uint8_t> splitMessageBuf;
|
|
||||||
bool shouldResetSplitMessageBuf = false;
|
|
||||||
|
|
||||||
bool isProcessingComplete = false;
|
private:
|
||||||
void processData();
|
std::unique_ptr<Game> game;
|
||||||
};
|
std::unique_ptr<std::ifstream> file;
|
||||||
}
|
std::vector<uint8_t> rawData;
|
||||||
|
std::string path;
|
||||||
|
std::ofstream log;
|
||||||
|
std::vector<uint8_t> splitMessageBuf;
|
||||||
|
bool shouldResetSplitMessageBuf = false;
|
||||||
|
|
||||||
|
bool isProcessingComplete = false;
|
||||||
|
void processData();
|
||||||
|
};
|
||||||
|
} // namespace Slippi
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,9 +7,8 @@
|
||||||
#include <SlippiGame.h>
|
#include <SlippiGame.h>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/FileUtil.h"
|
|
||||||
#include "Common/File.h"
|
#include "Common/File.h"
|
||||||
#include "EXI_Device.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Core/Slippi/SlippiGameFileLoader.h"
|
#include "Core/Slippi/SlippiGameFileLoader.h"
|
||||||
#include "Core/Slippi/SlippiMatchmaking.h"
|
#include "Core/Slippi/SlippiMatchmaking.h"
|
||||||
#include "Core/Slippi/SlippiNetplay.h"
|
#include "Core/Slippi/SlippiNetplay.h"
|
||||||
|
@ -17,6 +16,7 @@
|
||||||
#include "Core/Slippi/SlippiReplayComm.h"
|
#include "Core/Slippi/SlippiReplayComm.h"
|
||||||
#include "Core/Slippi/SlippiSavestate.h"
|
#include "Core/Slippi/SlippiSavestate.h"
|
||||||
#include "Core/Slippi/SlippiUser.h"
|
#include "Core/Slippi/SlippiUser.h"
|
||||||
|
#include "EXI_Device.h"
|
||||||
|
|
||||||
#define ROLLBACK_MAX_FRAMES 7
|
#define ROLLBACK_MAX_FRAMES 7
|
||||||
#define MAX_NAME_LENGTH 15
|
#define MAX_NAME_LENGTH 15
|
||||||
|
@ -24,65 +24,65 @@
|
||||||
|
|
||||||
namespace ExpansionInterface
|
namespace ExpansionInterface
|
||||||
{
|
{
|
||||||
// Emulated Slippi device used to receive and respond to in-game messages
|
// Emulated Slippi device used to receive and respond to in-game messages
|
||||||
class CEXISlippi : public IEXIDevice
|
class CEXISlippi : public IEXIDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CEXISlippi();
|
||||||
|
virtual ~CEXISlippi();
|
||||||
|
|
||||||
|
void DMAWrite(u32 _uAddr, u32 _uSize) override;
|
||||||
|
void DMARead(u32 addr, u32 size) override;
|
||||||
|
|
||||||
|
bool IsPresent() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum
|
||||||
{
|
{
|
||||||
public:
|
CMD_UNKNOWN = 0x0,
|
||||||
CEXISlippi();
|
|
||||||
virtual ~CEXISlippi();
|
|
||||||
|
|
||||||
void DMAWrite(u32 _uAddr, u32 _uSize) override;
|
// Recording
|
||||||
void DMARead(u32 addr, u32 size) override;
|
CMD_RECEIVE_COMMANDS = 0x35,
|
||||||
|
CMD_RECEIVE_GAME_INFO = 0x36,
|
||||||
|
CMD_RECEIVE_POST_FRAME_UPDATE = 0x38,
|
||||||
|
CMD_RECEIVE_GAME_END = 0x39,
|
||||||
|
|
||||||
bool IsPresent() const override;
|
// Playback
|
||||||
|
CMD_PREPARE_REPLAY = 0x75,
|
||||||
|
CMD_READ_FRAME = 0x76,
|
||||||
|
CMD_GET_LOCATION = 0x77,
|
||||||
|
CMD_IS_FILE_READY = 0x88,
|
||||||
|
CMD_IS_STOCK_STEAL = 0x89,
|
||||||
|
CMD_GET_GECKO_CODES = 0x8A,
|
||||||
|
|
||||||
private:
|
// Online
|
||||||
enum
|
CMD_ONLINE_INPUTS = 0xB0,
|
||||||
{
|
CMD_CAPTURE_SAVESTATE = 0xB1,
|
||||||
CMD_UNKNOWN = 0x0,
|
CMD_LOAD_SAVESTATE = 0xB2,
|
||||||
|
CMD_GET_MATCH_STATE = 0xB3,
|
||||||
|
CMD_FIND_OPPONENT = 0xB4,
|
||||||
|
CMD_SET_MATCH_SELECTIONS = 0xB5,
|
||||||
|
CMD_OPEN_LOGIN = 0xB6,
|
||||||
|
CMD_LOGOUT = 0xB7,
|
||||||
|
CMD_UPDATE = 0xB8,
|
||||||
|
CMD_GET_ONLINE_STATUS = 0xB9,
|
||||||
|
CMD_CLEANUP_CONNECTION = 0xBA,
|
||||||
|
|
||||||
// Recording
|
// Misc
|
||||||
CMD_RECEIVE_COMMANDS = 0x35,
|
CMD_LOG_MESSAGE = 0xD0,
|
||||||
CMD_RECEIVE_GAME_INFO = 0x36,
|
CMD_FILE_LENGTH = 0xD1,
|
||||||
CMD_RECEIVE_POST_FRAME_UPDATE = 0x38,
|
CMD_FILE_LOAD = 0xD2,
|
||||||
CMD_RECEIVE_GAME_END = 0x39,
|
};
|
||||||
|
|
||||||
// Playback
|
enum
|
||||||
CMD_PREPARE_REPLAY = 0x75,
|
{
|
||||||
CMD_READ_FRAME = 0x76,
|
FRAME_RESP_WAIT = 0,
|
||||||
CMD_GET_LOCATION = 0x77,
|
FRAME_RESP_CONTINUE = 1,
|
||||||
CMD_IS_FILE_READY = 0x88,
|
FRAME_RESP_TERMINATE = 2,
|
||||||
CMD_IS_STOCK_STEAL = 0x89,
|
FRAME_RESP_FASTFORWARD = 3,
|
||||||
CMD_GET_GECKO_CODES = 0x8A,
|
};
|
||||||
|
|
||||||
// Online
|
std::unordered_map<u8, u32> payloadSizes = {
|
||||||
CMD_ONLINE_INPUTS = 0xB0,
|
|
||||||
CMD_CAPTURE_SAVESTATE = 0xB1,
|
|
||||||
CMD_LOAD_SAVESTATE = 0xB2,
|
|
||||||
CMD_GET_MATCH_STATE = 0xB3,
|
|
||||||
CMD_FIND_OPPONENT = 0xB4,
|
|
||||||
CMD_SET_MATCH_SELECTIONS = 0xB5,
|
|
||||||
CMD_OPEN_LOGIN = 0xB6,
|
|
||||||
CMD_LOGOUT = 0xB7,
|
|
||||||
CMD_UPDATE = 0xB8,
|
|
||||||
CMD_GET_ONLINE_STATUS = 0xB9,
|
|
||||||
CMD_CLEANUP_CONNECTION = 0xBA,
|
|
||||||
|
|
||||||
// Misc
|
|
||||||
CMD_LOG_MESSAGE = 0xD0,
|
|
||||||
CMD_FILE_LENGTH = 0xD1,
|
|
||||||
CMD_FILE_LOAD = 0xD2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
FRAME_RESP_WAIT = 0,
|
|
||||||
FRAME_RESP_CONTINUE = 1,
|
|
||||||
FRAME_RESP_TERMINATE = 2,
|
|
||||||
FRAME_RESP_FASTFORWARD = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<u8, u32> payloadSizes = {
|
|
||||||
// The actual size of this command will be sent in one byte
|
// The actual size of this command will be sent in one byte
|
||||||
// after the command is received. The other receive command IDs
|
// after the command is received. The other receive command IDs
|
||||||
// and sizes will be received immediately following
|
// and sizes will be received immediately following
|
||||||
|
@ -111,115 +111,115 @@ namespace ExpansionInterface
|
||||||
{CMD_CLEANUP_CONNECTION, 0},
|
{CMD_CLEANUP_CONNECTION, 0},
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
{CMD_LOG_MESSAGE, 0xFFFF}, // Variable size... will only work if by itself
|
{CMD_LOG_MESSAGE, 0xFFFF}, // Variable size... will only work if by itself
|
||||||
{CMD_FILE_LENGTH, 0x40},
|
{CMD_FILE_LENGTH, 0x40},
|
||||||
{CMD_FILE_LOAD, 0x40},
|
{CMD_FILE_LOAD, 0x40},
|
||||||
};
|
|
||||||
|
|
||||||
struct WriteMessage
|
|
||||||
{
|
|
||||||
std::vector<u8> data;
|
|
||||||
std::string operation;
|
|
||||||
};
|
|
||||||
|
|
||||||
// .slp File creation stuff
|
|
||||||
u32 writtenByteCount = 0;
|
|
||||||
|
|
||||||
// vars for metadata generation
|
|
||||||
time_t gameStartTime;
|
|
||||||
s32 lastFrame;
|
|
||||||
std::unordered_map<u8, std::unordered_map<u8, u32>> characterUsage;
|
|
||||||
|
|
||||||
void updateMetadataFields(u8* payload, u32 length);
|
|
||||||
void configureCommands(u8* payload, u8 length);
|
|
||||||
void writeToFileAsync(u8* payload, u32 length, std::string fileOption);
|
|
||||||
void writeToFile(std::unique_ptr<WriteMessage> msg);
|
|
||||||
std::vector<u8> generateMetadata();
|
|
||||||
void createNewFile();
|
|
||||||
void closeFile();
|
|
||||||
std::string generateFileName();
|
|
||||||
bool checkFrameFullyFetched(s32 frameIndex);
|
|
||||||
bool shouldFFWFrame(s32 frameIndex);
|
|
||||||
|
|
||||||
// std::ofstream log;
|
|
||||||
|
|
||||||
File::IOFile m_file;
|
|
||||||
std::vector<u8> m_payload;
|
|
||||||
|
|
||||||
// online play stuff
|
|
||||||
u16 getRandomStage();
|
|
||||||
void handleOnlineInputs(u8* payload);
|
|
||||||
void prepareOpponentInputs(u8* payload);
|
|
||||||
void handleSendInputs(u8* payload);
|
|
||||||
void handleCaptureSavestate(u8* payload);
|
|
||||||
void handleLoadSavestate(u8* payload);
|
|
||||||
void startFindMatch(u8* payload);
|
|
||||||
void prepareOnlineMatchState();
|
|
||||||
void setMatchSelections(u8* payload);
|
|
||||||
bool shouldSkipOnlineFrame(s32 frame);
|
|
||||||
void handleLogInRequest();
|
|
||||||
void handleLogOutRequest();
|
|
||||||
void handleUpdateAppRequest();
|
|
||||||
void prepareOnlineStatus();
|
|
||||||
void handleConnectionCleanup();
|
|
||||||
|
|
||||||
// replay playback stuff
|
|
||||||
void prepareGameInfo(u8* payload);
|
|
||||||
void prepareGeckoList();
|
|
||||||
void prepareCharacterFrameData(Slippi::FrameData* frame, u8 port, u8 isFollower);
|
|
||||||
void prepareFrameData(u8* payload);
|
|
||||||
void prepareIsStockSteal(u8* payload);
|
|
||||||
void prepareIsFileReady();
|
|
||||||
|
|
||||||
// misc stuff
|
|
||||||
void logMessageFromGame(u8* payload);
|
|
||||||
void prepareFileLength(u8* payload);
|
|
||||||
void prepareFileLoad(u8* payload);
|
|
||||||
|
|
||||||
void FileWriteThread(void);
|
|
||||||
|
|
||||||
std::queue<std::unique_ptr<WriteMessage>> fileWriteQueue;
|
|
||||||
bool writeThreadRunning = false;
|
|
||||||
std::thread m_fileWriteThread;
|
|
||||||
|
|
||||||
std::unordered_map<u8, std::string> getNetplayNames();
|
|
||||||
|
|
||||||
std::vector<u8> playbackSavestatePayload;
|
|
||||||
std::vector<u8> geckoList;
|
|
||||||
|
|
||||||
u32 stallFrameCount = 0;
|
|
||||||
bool isConnectionStalled = false;
|
|
||||||
|
|
||||||
std::vector<u8> m_read_queue;
|
|
||||||
std::unique_ptr<Slippi::SlippiGame> m_current_game = nullptr;
|
|
||||||
SlippiMatchmaking::MatchSearchSettings lastSearch;
|
|
||||||
|
|
||||||
std::vector<u16> stagePool;
|
|
||||||
|
|
||||||
u32 frameSeqIdx = 0;
|
|
||||||
|
|
||||||
bool isEnetInitialized = false;
|
|
||||||
|
|
||||||
std::default_random_engine generator;
|
|
||||||
|
|
||||||
std::string forcedError = "";
|
|
||||||
|
|
||||||
// Frame skipping variables
|
|
||||||
int framesToSkip = 0;
|
|
||||||
bool isCurrentlySkipping = false;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void TransferByte(u8& byte) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
SlippiPlayerSelections localSelections;
|
|
||||||
|
|
||||||
std::unique_ptr<SlippiUser> user;
|
|
||||||
std::unique_ptr<SlippiGameFileLoader> gameFileLoader;
|
|
||||||
std::unique_ptr<SlippiNetplayClient> slippi_netplay;
|
|
||||||
std::unique_ptr<SlippiMatchmaking> matchmaking;
|
|
||||||
|
|
||||||
std::map<s32, std::unique_ptr<SlippiSavestate>> activeSavestates;
|
|
||||||
std::deque<std::unique_ptr<SlippiSavestate>> availableSavestates;
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
struct WriteMessage
|
||||||
|
{
|
||||||
|
std::vector<u8> data;
|
||||||
|
std::string operation;
|
||||||
|
};
|
||||||
|
|
||||||
|
// .slp File creation stuff
|
||||||
|
u32 writtenByteCount = 0;
|
||||||
|
|
||||||
|
// vars for metadata generation
|
||||||
|
time_t gameStartTime;
|
||||||
|
s32 lastFrame;
|
||||||
|
std::unordered_map<u8, std::unordered_map<u8, u32>> characterUsage;
|
||||||
|
|
||||||
|
void updateMetadataFields(u8* payload, u32 length);
|
||||||
|
void configureCommands(u8* payload, u8 length);
|
||||||
|
void writeToFileAsync(u8* payload, u32 length, std::string fileOption);
|
||||||
|
void writeToFile(std::unique_ptr<WriteMessage> msg);
|
||||||
|
std::vector<u8> generateMetadata();
|
||||||
|
void createNewFile();
|
||||||
|
void closeFile();
|
||||||
|
std::string generateFileName();
|
||||||
|
bool checkFrameFullyFetched(s32 frameIndex);
|
||||||
|
bool shouldFFWFrame(s32 frameIndex);
|
||||||
|
|
||||||
|
// std::ofstream log;
|
||||||
|
|
||||||
|
File::IOFile m_file;
|
||||||
|
std::vector<u8> m_payload;
|
||||||
|
|
||||||
|
// online play stuff
|
||||||
|
u16 getRandomStage();
|
||||||
|
void handleOnlineInputs(u8* payload);
|
||||||
|
void prepareOpponentInputs(u8* payload);
|
||||||
|
void handleSendInputs(u8* payload);
|
||||||
|
void handleCaptureSavestate(u8* payload);
|
||||||
|
void handleLoadSavestate(u8* payload);
|
||||||
|
void startFindMatch(u8* payload);
|
||||||
|
void prepareOnlineMatchState();
|
||||||
|
void setMatchSelections(u8* payload);
|
||||||
|
bool shouldSkipOnlineFrame(s32 frame);
|
||||||
|
void handleLogInRequest();
|
||||||
|
void handleLogOutRequest();
|
||||||
|
void handleUpdateAppRequest();
|
||||||
|
void prepareOnlineStatus();
|
||||||
|
void handleConnectionCleanup();
|
||||||
|
|
||||||
|
// replay playback stuff
|
||||||
|
void prepareGameInfo(u8* payload);
|
||||||
|
void prepareGeckoList();
|
||||||
|
void prepareCharacterFrameData(Slippi::FrameData* frame, u8 port, u8 isFollower);
|
||||||
|
void prepareFrameData(u8* payload);
|
||||||
|
void prepareIsStockSteal(u8* payload);
|
||||||
|
void prepareIsFileReady();
|
||||||
|
|
||||||
|
// misc stuff
|
||||||
|
void logMessageFromGame(u8* payload);
|
||||||
|
void prepareFileLength(u8* payload);
|
||||||
|
void prepareFileLoad(u8* payload);
|
||||||
|
|
||||||
|
void FileWriteThread(void);
|
||||||
|
|
||||||
|
std::queue<std::unique_ptr<WriteMessage>> fileWriteQueue;
|
||||||
|
bool writeThreadRunning = false;
|
||||||
|
std::thread m_fileWriteThread;
|
||||||
|
|
||||||
|
std::unordered_map<u8, std::string> getNetplayNames();
|
||||||
|
|
||||||
|
std::vector<u8> playbackSavestatePayload;
|
||||||
|
std::vector<u8> geckoList;
|
||||||
|
|
||||||
|
u32 stallFrameCount = 0;
|
||||||
|
bool isConnectionStalled = false;
|
||||||
|
|
||||||
|
std::vector<u8> m_read_queue;
|
||||||
|
std::unique_ptr<Slippi::SlippiGame> m_current_game = nullptr;
|
||||||
|
SlippiMatchmaking::MatchSearchSettings lastSearch;
|
||||||
|
|
||||||
|
std::vector<u16> stagePool;
|
||||||
|
|
||||||
|
u32 frameSeqIdx = 0;
|
||||||
|
|
||||||
|
bool isEnetInitialized = false;
|
||||||
|
|
||||||
|
std::default_random_engine generator;
|
||||||
|
|
||||||
|
std::string forcedError = "";
|
||||||
|
|
||||||
|
// Frame skipping variables
|
||||||
|
int framesToSkip = 0;
|
||||||
|
bool isCurrentlySkipping = false;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void TransferByte(u8& byte) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SlippiPlayerSelections localSelections;
|
||||||
|
|
||||||
|
std::unique_ptr<SlippiUser> user;
|
||||||
|
std::unique_ptr<SlippiGameFileLoader> gameFileLoader;
|
||||||
|
std::unique_ptr<SlippiNetplayClient> slippi_netplay;
|
||||||
|
std::unique_ptr<SlippiMatchmaking> matchmaking;
|
||||||
|
|
||||||
|
std::map<s32, std::unique_ptr<SlippiSavestate>> activeSavestates;
|
||||||
|
std::deque<std::unique_ptr<SlippiSavestate>> availableSavestates;
|
||||||
|
};
|
||||||
|
} // namespace ExpansionInterface
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
#include "SlippiGameFileLoader.h"
|
#include "SlippiGameFileLoader.h"
|
||||||
|
|
||||||
#include "Common/Logging/Log.h"
|
|
||||||
#include "Common/FileUtil.h"
|
|
||||||
#include "Common/File.h"
|
#include "Common/File.h"
|
||||||
#include "Core/HW/DVD/DVDThread.h"
|
#include "Common/FileUtil.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
#include "Core/Boot/Boot.h"
|
#include "Core/Boot/Boot.h"
|
||||||
#include "Core/Core.h"
|
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
|
#include "Core/Core.h"
|
||||||
|
#include "Core/HW/DVD/DVDThread.h"
|
||||||
|
|
||||||
std::string getFilePath(std::string fileName)
|
std::string getFilePath(std::string fileName)
|
||||||
{
|
{
|
||||||
std::string dirPath = File::GetSysDirectory();
|
std::string dirPath = File::GetSysDirectory();
|
||||||
|
|
||||||
std::string filePath = dirPath + "GameFiles/GALE01/" + fileName; // TODO: Handle other games?
|
std::string filePath = dirPath + "GameFiles/GALE01/" + fileName; // TODO: Handle other games?
|
||||||
|
|
||||||
if (File::Exists(filePath))
|
if (File::Exists(filePath))
|
||||||
{
|
{
|
||||||
|
@ -49,8 +49,10 @@ u32 SlippiGameFileLoader::LoadFile(std::string fileName, std::string& data)
|
||||||
std::string fileContents;
|
std::string fileContents;
|
||||||
File::ReadFileToString(gameFilePath, fileContents);
|
File::ReadFileToString(gameFilePath, fileContents);
|
||||||
|
|
||||||
// If the file was a diff file and the game is running, load the main file from ISO and apply patch
|
// If the file was a diff file and the game is running, load the main file from ISO and apply
|
||||||
if (gameFilePath.substr(gameFilePath.length() - 5) == ".diff" && Core::GetState() == Core::State::Running)
|
// patch
|
||||||
|
if (gameFilePath.substr(gameFilePath.length() - 5) == ".diff" &&
|
||||||
|
Core::GetState() == Core::State::Running)
|
||||||
{
|
{
|
||||||
std::vector<u8> buf;
|
std::vector<u8> buf;
|
||||||
INFO_LOG(SLIPPI, "Will process diff");
|
INFO_LOG(SLIPPI, "Will process diff");
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include <open-vcdiff/src/google/vcdecoder.h>
|
#include <open-vcdiff/src/google/vcdecoder.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
class SlippiGameFileLoader
|
class SlippiGameFileLoader
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#include "SlippiMatchmaking.h"
|
#include "SlippiMatchmaking.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include "Common/Common.h"
|
#include "Common/Common.h"
|
||||||
#include "Common/Version.h"
|
|
||||||
#include "Common/ENetUtil.h"
|
#include "Common/ENetUtil.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#include <string>
|
#include "Common/Version.h"
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class MmMessageType
|
class MmMessageType
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,8 @@ SlippiMatchmaking::SlippiMatchmaking(SlippiUser* user)
|
||||||
m_client = nullptr;
|
m_client = nullptr;
|
||||||
m_server = nullptr;
|
m_server = nullptr;
|
||||||
|
|
||||||
MM_HOST = Common::scm_slippi_semver_str.find("dev") == std::string::npos ? MM_HOST_PROD : MM_HOST_DEV;
|
MM_HOST =
|
||||||
|
Common::scm_slippi_semver_str.find("dev") == std::string::npos ? MM_HOST_PROD : MM_HOST_DEV;
|
||||||
|
|
||||||
generator = std::default_random_engine(Common::Timer::GetTimeMs());
|
generator = std::default_random_engine(Common::Timer::GetTimeMs());
|
||||||
}
|
}
|
||||||
|
@ -111,7 +112,8 @@ int SlippiMatchmaking::receiveMessage(json& msg, int timeoutMs)
|
||||||
case ENET_EVENT_TYPE_RECEIVE:
|
case ENET_EVENT_TYPE_RECEIVE:
|
||||||
{
|
{
|
||||||
std::vector<u8> buf;
|
std::vector<u8> buf;
|
||||||
buf.insert(buf.end(), netEvent.packet->data, netEvent.packet->data + netEvent.packet->dataLength);
|
buf.insert(buf.end(), netEvent.packet->data,
|
||||||
|
netEvent.packet->data + netEvent.packet->dataLength);
|
||||||
|
|
||||||
std::string str(buf.begin(), buf.end());
|
std::string str(buf.begin(), buf.end());
|
||||||
INFO_LOG(SLIPPI_ONLINE, "[Matchmaking] Received: %s", str.c_str());
|
INFO_LOG(SLIPPI_ONLINE, "[Matchmaking] Received: %s", str.c_str());
|
||||||
|
@ -281,13 +283,13 @@ void SlippiMatchmaking::startMatchmaking()
|
||||||
|
|
||||||
std::vector<u8> connectCodeBuf;
|
std::vector<u8> connectCodeBuf;
|
||||||
connectCodeBuf.insert(connectCodeBuf.end(), m_searchSettings.connectCode.begin(),
|
connectCodeBuf.insert(connectCodeBuf.end(), m_searchSettings.connectCode.begin(),
|
||||||
m_searchSettings.connectCode.end());
|
m_searchSettings.connectCode.end());
|
||||||
|
|
||||||
// Send message to server to create ticket
|
// Send message to server to create ticket
|
||||||
json request;
|
json request;
|
||||||
request["type"] = MmMessageType::CREATE_TICKET;
|
request["type"] = MmMessageType::CREATE_TICKET;
|
||||||
request["user"] = { {"uid", userInfo.uid}, {"playKey", userInfo.playKey} };
|
request["user"] = {{"uid", userInfo.uid}, {"playKey", userInfo.playKey}};
|
||||||
request["search"] = { {"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf} };
|
request["search"] = {{"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf}};
|
||||||
request["appVersion"] = Common::scm_slippi_semver_str;
|
request["appVersion"] = Common::scm_slippi_semver_str;
|
||||||
sendMessage(request);
|
sendMessage(request);
|
||||||
|
|
||||||
|
@ -296,7 +298,8 @@ void SlippiMatchmaking::startMatchmaking()
|
||||||
int rcvRes = receiveMessage(response, 5000);
|
int rcvRes = receiveMessage(response, 5000);
|
||||||
if (rcvRes != 0)
|
if (rcvRes != 0)
|
||||||
{
|
{
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Did not receive response from server for create ticket");
|
ERROR_LOG(SLIPPI_ONLINE,
|
||||||
|
"[Matchmaking] Did not receive response from server for create ticket");
|
||||||
m_state = ProcessState::ERROR_ENCOUNTERED;
|
m_state = ProcessState::ERROR_ENCOUNTERED;
|
||||||
m_errorMsg = "Failed to join mm queue";
|
m_errorMsg = "Failed to join mm queue";
|
||||||
return;
|
return;
|
||||||
|
@ -364,7 +367,8 @@ void SlippiMatchmaking::handleMatchmaking()
|
||||||
if (latestVersion != "")
|
if (latestVersion != "")
|
||||||
{
|
{
|
||||||
// Update file to get new version number when the mm server tells us our version is outdated
|
// Update file to get new version number when the mm server tells us our version is outdated
|
||||||
m_user->OverwriteLatestVersion(latestVersion); // Force latest version for people whose file updates dont work
|
m_user->OverwriteLatestVersion(
|
||||||
|
latestVersion); // Force latest version for people whose file updates dont work
|
||||||
}
|
}
|
||||||
|
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Received error from server for get ticket");
|
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Received error from server for get ticket");
|
||||||
|
@ -382,7 +386,8 @@ void SlippiMatchmaking::handleMatchmaking()
|
||||||
terminateMmConnection();
|
terminateMmConnection();
|
||||||
|
|
||||||
m_state = ProcessState::OPPONENT_CONNECTING;
|
m_state = ProcessState::OPPONENT_CONNECTING;
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Opponent found. isDecider: %s", m_isHost ? "true" : "false");
|
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Opponent found. isDecider: %s",
|
||||||
|
m_isHost ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlippiMatchmaking::handleConnecting()
|
void SlippiMatchmaking::handleConnecting()
|
||||||
|
@ -390,7 +395,8 @@ void SlippiMatchmaking::handleConnecting()
|
||||||
std::vector<std::string> ipParts = SplitString(m_oppIp, ':');
|
std::vector<std::string> ipParts = SplitString(m_oppIp, ':');
|
||||||
|
|
||||||
// Is host is now used to specify who the decider is
|
// Is host is now used to specify who the decider is
|
||||||
auto client = std::make_unique<SlippiNetplayClient>(ipParts[0], std::stoi(ipParts[1]), m_hostPort, m_isHost);
|
auto client = std::make_unique<SlippiNetplayClient>(ipParts[0], std::stoi(ipParts[1]), m_hostPort,
|
||||||
|
m_isHost);
|
||||||
|
|
||||||
while (!m_netplayClient)
|
while (!m_netplayClient)
|
||||||
{
|
{
|
||||||
|
@ -408,7 +414,8 @@ void SlippiMatchmaking::handleConnecting()
|
||||||
}
|
}
|
||||||
else if (status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED)
|
else if (status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED)
|
||||||
{
|
{
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Connection attempt failed, looking for someone else.");
|
ERROR_LOG(SLIPPI_ONLINE,
|
||||||
|
"[Matchmaking] Connection attempt failed, looking for someone else.");
|
||||||
|
|
||||||
// Return to the start to get a new ticket to find someone else we can hopefully connect with
|
// Return to the start to get a new ticket to find someone else we can hopefully connect with
|
||||||
m_netplayClient = nullptr;
|
m_netplayClient = nullptr;
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
#include "Core/Slippi/SlippiUser.h"
|
#include "Core/Slippi/SlippiUser.h"
|
||||||
|
|
||||||
#include <enet/enet.h>
|
#include <enet/enet.h>
|
||||||
|
#include <random>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <random>
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
@ -50,8 +50,8 @@ public:
|
||||||
std::string GetErrorMessage();
|
std::string GetErrorMessage();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const std::string MM_HOST_DEV = "35.197.121.196"; // Dev host
|
const std::string MM_HOST_DEV = "35.197.121.196"; // Dev host
|
||||||
const std::string MM_HOST_PROD = "35.247.98.48"; // Production host
|
const std::string MM_HOST_PROD = "35.247.98.48"; // Production host
|
||||||
const u16 MM_PORT = 43113;
|
const u16 MM_PORT = 43113;
|
||||||
|
|
||||||
std::string MM_HOST = "";
|
std::string MM_HOST = "";
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
#include "Common/MD5.h"
|
#include "Common/MD5.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Common/Timer.h"
|
#include "Common/Timer.h"
|
||||||
#include "Core/ConfigManager.h"
|
|
||||||
#include "Core/NetPlayProto.h"
|
|
||||||
#include "Core/Core.h"
|
|
||||||
#include "Core/Config/NetplaySettings.h"
|
#include "Core/Config/NetplaySettings.h"
|
||||||
|
#include "Core/ConfigManager.h"
|
||||||
|
#include "Core/Core.h"
|
||||||
|
#include "Core/NetPlayProto.h"
|
||||||
//#include "Core/HW/EXI_DeviceIPL.h"
|
//#include "Core/HW/EXI_DeviceIPL.h"
|
||||||
//#include "Core/HW/SI.h"
|
//#include "Core/HW/SI.h"
|
||||||
//#include "Core/HW/SI_DeviceGCController.h"
|
//#include "Core/HW/SI_DeviceGCController.h"
|
||||||
|
@ -22,16 +22,16 @@
|
||||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||||
//#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_bt_emu.h"
|
//#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_bt_emu.h"
|
||||||
#include "Core/Movie.h"
|
|
||||||
#include "InputCommon/GCAdapter.h"
|
|
||||||
#include "VideoCommon/OnScreenDisplay.h"
|
|
||||||
#include "VideoCommon/VideoConfig.h"
|
|
||||||
#include <SlippiGame.h>
|
#include <SlippiGame.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <mbedtls/md5.h>
|
#include <mbedtls/md5.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include "Core/Movie.h"
|
||||||
|
#include "InputCommon/GCAdapter.h"
|
||||||
|
#include "VideoCommon/OnScreenDisplay.h"
|
||||||
|
#include "VideoCommon/VideoConfig.h"
|
||||||
|
|
||||||
static std::mutex pad_mutex;
|
static std::mutex pad_mutex;
|
||||||
static std::mutex ack_mutex;
|
static std::mutex ack_mutex;
|
||||||
|
@ -62,15 +62,14 @@ SlippiNetplayClient::~SlippiNetplayClient()
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from ---SLIPPI EXI--- thread
|
// called from ---SLIPPI EXI--- thread
|
||||||
SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort,
|
SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 remotePort,
|
||||||
bool isDecider)
|
const u16 localPort, bool isDecider)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
: m_qos_handle(nullptr)
|
: m_qos_handle(nullptr), m_qos_flow_id(0)
|
||||||
, m_qos_flow_id(0)
|
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
WARN_LOG(SLIPPI_ONLINE, "Initializing Slippi Netplay for port: %d, with host: %s", localPort,
|
WARN_LOG(SLIPPI_ONLINE, "Initializing Slippi Netplay for port: %d, with host: %s", localPort,
|
||||||
isDecider ? "true" : "false");
|
isDecider ? "true" : "false");
|
||||||
|
|
||||||
this->isDecider = isDecider;
|
this->isDecider = isDecider;
|
||||||
|
|
||||||
|
@ -78,9 +77,10 @@ SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 r
|
||||||
ENetAddress* localAddr = nullptr;
|
ENetAddress* localAddr = nullptr;
|
||||||
ENetAddress localAddrDef;
|
ENetAddress localAddrDef;
|
||||||
|
|
||||||
// It is important to be able to set the local port to listen on even in a client connection because
|
// It is important to be able to set the local port to listen on even in a client connection
|
||||||
// not doing so will break hole punching, the host is expecting traffic to come from a specific ip/port
|
// because not doing so will break hole punching, the host is expecting traffic to come from a
|
||||||
// and if the port does not match what it is expecting, it will not get through the NAT on some routers
|
// specific ip/port and if the port does not match what it is expecting, it will not get through
|
||||||
|
// the NAT on some routers
|
||||||
if (localPort > 0)
|
if (localPort > 0)
|
||||||
{
|
{
|
||||||
INFO_LOG(SLIPPI_ONLINE, "Setting up local address");
|
INFO_LOG(SLIPPI_ONLINE, "Setting up local address");
|
||||||
|
@ -91,7 +91,8 @@ SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 r
|
||||||
localAddr = &localAddrDef;
|
localAddr = &localAddrDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Figure out how to use a local port when not hosting without accepting incoming connections
|
// TODO: Figure out how to use a local port when not hosting without accepting incoming
|
||||||
|
// connections
|
||||||
m_client = enet_host_create(localAddr, 2, 3, 0, 0);
|
m_client = enet_host_create(localAddr, 2, 3, 0, 0);
|
||||||
|
|
||||||
if (m_client == nullptr)
|
if (m_client == nullptr)
|
||||||
|
@ -126,7 +127,8 @@ SlippiNetplayClient::SlippiNetplayClient(bool isDecider)
|
||||||
unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||||
{
|
{
|
||||||
NetPlay::MessageId mid = 0;
|
NetPlay::MessageId mid = 0;
|
||||||
if (!(packet >> mid)) {
|
if (!(packet >> mid))
|
||||||
|
{
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "Received empty netplay packet");
|
ERROR_LOG(SLIPPI_ONLINE, "Received empty netplay packet");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -136,7 +138,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||||
case NetPlay::NP_MSG_SLIPPI_PAD:
|
case NetPlay::NP_MSG_SLIPPI_PAD:
|
||||||
{
|
{
|
||||||
int32_t frame;
|
int32_t frame;
|
||||||
if (!(packet >> frame)) {
|
if (!(packet >> frame))
|
||||||
|
{
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read frame count");
|
ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read frame count");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -151,8 +154,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||||
auto timing = lastFrameTiming;
|
auto timing = lastFrameTiming;
|
||||||
if (!hasGameStarted)
|
if (!hasGameStarted)
|
||||||
{
|
{
|
||||||
// Handle case where opponent starts sending inputs before our game has reached frame 1. This will
|
// Handle case where opponent starts sending inputs before our game has reached frame 1. This
|
||||||
// continuously say frame 0 is now to prevent opp from getting too far ahead
|
// will continuously say frame 0 is now to prevent opp from getting too far ahead
|
||||||
timing.frame = 0;
|
timing.frame = 0;
|
||||||
timing.timeUs = curTime;
|
timing.timeUs = curTime;
|
||||||
}
|
}
|
||||||
|
@ -161,8 +164,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||||
s64 frameDiffOffsetUs = 16683 * (timing.frame - frame);
|
s64 frameDiffOffsetUs = 16683 * (timing.frame - frame);
|
||||||
s64 timeOffsetUs = opponentSendTimeUs - timing.timeUs + frameDiffOffsetUs;
|
s64 timeOffsetUs = opponentSendTimeUs - timing.timeUs + frameDiffOffsetUs;
|
||||||
|
|
||||||
INFO_LOG(SLIPPI_ONLINE, "[Offset] Opp Frame: %d, My Frame: %d. Time offset: %lld", frame, timing.frame,
|
INFO_LOG(SLIPPI_ONLINE, "[Offset] Opp Frame: %d, My Frame: %d. Time offset: %lld", frame,
|
||||||
timeOffsetUs);
|
timing.frame, timeOffsetUs);
|
||||||
|
|
||||||
// Add this offset to circular buffer for use later
|
// Add this offset to circular buffer for use later
|
||||||
if (frameOffsetData.buf.size() < SLIPPI_ONLINE_LOCKSTEP_INTERVAL)
|
if (frameOffsetData.buf.size() < SLIPPI_ONLINE_LOCKSTEP_INTERVAL)
|
||||||
|
@ -173,7 +176,7 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||||
frameOffsetData.idx = (frameOffsetData.idx + 1) % SLIPPI_ONLINE_LOCKSTEP_INTERVAL;
|
frameOffsetData.idx = (frameOffsetData.idx + 1) % SLIPPI_ONLINE_LOCKSTEP_INTERVAL;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(pad_mutex); // TODO: Is this the correct lock?
|
std::lock_guard<std::mutex> lk(pad_mutex); // TODO: Is this the correct lock?
|
||||||
|
|
||||||
auto packetData = (u8*)packet.getData();
|
auto packetData = (u8*)packet.getData();
|
||||||
|
|
||||||
|
@ -183,18 +186,20 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||||
int inputsToCopy = frame - headFrame;
|
int inputsToCopy = frame - headFrame;
|
||||||
|
|
||||||
// Check that the packet actually contains the data it claims to
|
// Check that the packet actually contains the data it claims to
|
||||||
if ((5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE) > (int)packet.getDataSize()) {
|
if ((5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE) > (int)packet.getDataSize())
|
||||||
|
{
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read pad buffer");
|
ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read pad buffer");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = inputsToCopy - 1; i >= 0; i--)
|
for (int i = inputsToCopy - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
auto pad = std::make_unique<SlippiPad>(frame - i, &packetData[5 + i * SLIPPI_PAD_DATA_SIZE]);
|
auto pad =
|
||||||
|
std::make_unique<SlippiPad>(frame - i, &packetData[5 + i * SLIPPI_PAD_DATA_SIZE]);
|
||||||
|
|
||||||
INFO_LOG(SLIPPI_ONLINE, "Rcv [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", pad->frame,
|
INFO_LOG(SLIPPI_ONLINE, "Rcv [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", pad->frame,
|
||||||
pad->padBuf[0], pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5],
|
pad->padBuf[0], pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4],
|
||||||
pad->padBuf[6], pad->padBuf[7]);
|
pad->padBuf[5], pad->padBuf[6], pad->padBuf[7]);
|
||||||
|
|
||||||
remotePadQueue.push_front(std::move(pad));
|
remotePadQueue.push_front(std::move(pad));
|
||||||
}
|
}
|
||||||
|
@ -210,11 +215,12 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||||
|
|
||||||
case NetPlay::NP_MSG_SLIPPI_PAD_ACK:
|
case NetPlay::NP_MSG_SLIPPI_PAD_ACK:
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(ack_mutex); // Trying to fix rare crash on ackTimers.count
|
std::lock_guard<std::mutex> lk(ack_mutex); // Trying to fix rare crash on ackTimers.count
|
||||||
|
|
||||||
// Store last frame acked
|
// Store last frame acked
|
||||||
int32_t frame;
|
int32_t frame;
|
||||||
if (!(packet >> frame)) {
|
if (!(packet >> frame))
|
||||||
|
{
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "Ack packet too small to read frame");
|
ERROR_LOG(SLIPPI_ONLINE, "Ack packet too small to read frame");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -239,8 +245,9 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||||
pingUs = Common::Timer::GetTimeUs() - sendTime;
|
pingUs = Common::Timer::GetTimeUs() - sendTime;
|
||||||
if (g_ActiveConfig.bShowNetPlayPing && frame % SLIPPI_PING_DISPLAY_INTERVAL == 0)
|
if (g_ActiveConfig.bShowNetPlayPing && frame % SLIPPI_PING_DISPLAY_INTERVAL == 0)
|
||||||
{
|
{
|
||||||
OSD::AddTypedMessage(OSD::MessageType::NetPlayPing, StringFromFormat("Ping: %u", pingUs / 1000),
|
OSD::AddTypedMessage(OSD::MessageType::NetPlayPing,
|
||||||
OSD::Duration::NORMAL, OSD::Color::CYAN);
|
StringFromFormat("Ping: %u", pingUs / 1000), OSD::Duration::NORMAL,
|
||||||
|
OSD::Color::CYAN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -259,7 +266,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||||
|
|
||||||
// This might be a good place to reset some logic? Game can't start until we receive this msg
|
// This might be a good place to reset some logic? Game can't start until we receive this msg
|
||||||
// so this should ensure that everything is initialized before the game starts
|
// so this should ensure that everything is initialized before the game starts
|
||||||
// TODO: This could cause issues in the case of a desync? If this is ever received mid-game, bad things
|
// TODO: This could cause issues in the case of a desync? If this is ever received mid-game, bad
|
||||||
|
// things
|
||||||
// TODO: will happen. Consider improving this
|
// TODO: will happen. Consider improving this
|
||||||
hasGameStarted = false;
|
hasGameStarted = false;
|
||||||
}
|
}
|
||||||
|
@ -267,7 +275,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||||
|
|
||||||
case NetPlay::NP_MSG_SLIPPI_CONN_SELECTED:
|
case NetPlay::NP_MSG_SLIPPI_CONN_SELECTED:
|
||||||
{
|
{
|
||||||
// Currently this is unused but the intent is to support two-way simultaneous connection attempts
|
// Currently this is unused but the intent is to support two-way simultaneous connection
|
||||||
|
// attempts
|
||||||
isConnectionSelected = true;
|
isConnectionSelected = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -290,7 +299,8 @@ void SlippiNetplayClient::writeToPacket(sf::Packet& packet, SlippiPlayerSelectio
|
||||||
packet << s.connectCode;
|
packet << s.connectCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SlippiPlayerSelections> SlippiNetplayClient::readSelectionsFromPacket(sf::Packet& packet)
|
std::unique_ptr<SlippiPlayerSelections>
|
||||||
|
SlippiNetplayClient::readSelectionsFromPacket(sf::Packet& packet)
|
||||||
{
|
{
|
||||||
auto s = std::make_unique<SlippiPlayerSelections>();
|
auto s = std::make_unique<SlippiPlayerSelections>();
|
||||||
|
|
||||||
|
@ -405,26 +415,27 @@ void SlippiNetplayClient::ThreadFunc()
|
||||||
|
|
||||||
bool qos_success = false;
|
bool qos_success = false;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
QOS_VERSION ver = { 1, 0 };
|
QOS_VERSION ver = {1, 0};
|
||||||
|
|
||||||
if (Config::Get(Config::NETPLAY_ENABLE_QOS) && QOSCreateHandle(&ver, &m_qos_handle))
|
if (Config::Get(Config::NETPLAY_ENABLE_QOS) && QOSCreateHandle(&ver, &m_qos_handle))
|
||||||
{
|
{
|
||||||
// from win32.c
|
// from win32.c
|
||||||
struct sockaddr_in sin = { 0 };
|
struct sockaddr_in sin = {0};
|
||||||
|
|
||||||
sin.sin_family = AF_INET;
|
sin.sin_family = AF_INET;
|
||||||
sin.sin_port = ENET_HOST_TO_NET_16(m_server->host->address.port);
|
sin.sin_port = ENET_HOST_TO_NET_16(m_server->host->address.port);
|
||||||
sin.sin_addr.s_addr = m_server->host->address.host;
|
sin.sin_addr.s_addr = m_server->host->address.host;
|
||||||
|
|
||||||
if (QOSAddSocketToFlow(m_qos_handle, m_server->host->socket, reinterpret_cast<PSOCKADDR>(&sin),
|
if (QOSAddSocketToFlow(m_qos_handle, m_server->host->socket, reinterpret_cast<PSOCKADDR>(&sin),
|
||||||
// this is 0x38
|
// this is 0x38
|
||||||
QOSTrafficTypeControl, QOS_NON_ADAPTIVE_FLOW, &m_qos_flow_id))
|
QOSTrafficTypeControl, QOS_NON_ADAPTIVE_FLOW, &m_qos_flow_id))
|
||||||
{
|
{
|
||||||
DWORD dscp = 0x2e;
|
DWORD dscp = 0x2e;
|
||||||
|
|
||||||
// this will fail if we're not admin
|
// this will fail if we're not admin
|
||||||
// sets DSCP to the same as linux (0x2e)
|
// sets DSCP to the same as linux (0x2e)
|
||||||
QOSSetFlow(m_qos_handle, m_qos_flow_id, QOSSetOutgoingDSCPValue, sizeof(DWORD), &dscp, 0, nullptr);
|
QOSSetFlow(m_qos_handle, m_qos_flow_id, QOSSetOutgoingDSCPValue, sizeof(DWORD), &dscp, 0,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
qos_success = true;
|
qos_success = true;
|
||||||
}
|
}
|
||||||
|
@ -441,7 +452,8 @@ void SlippiNetplayClient::ThreadFunc()
|
||||||
// https://www.tucny.com/Home/dscp-tos
|
// https://www.tucny.com/Home/dscp-tos
|
||||||
// ef is better than cs7
|
// ef is better than cs7
|
||||||
int tos_val = 0xb8;
|
int tos_val = 0xb8;
|
||||||
qos_success = setsockopt(m_server->host->socket, IPPROTO_IP, IP_TOS, &tos_val, sizeof(tos_val)) == 0;
|
qos_success =
|
||||||
|
setsockopt(m_server->host->socket, IPPROTO_IP, IP_TOS, &tos_val, sizeof(tos_val)) == 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -468,13 +480,13 @@ void SlippiNetplayClient::ThreadFunc()
|
||||||
break;
|
break;
|
||||||
case ENET_EVENT_TYPE_DISCONNECT:
|
case ENET_EVENT_TYPE_DISCONNECT:
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "[Netplay] Disconnected Event detected: %s",
|
ERROR_LOG(SLIPPI_ONLINE, "[Netplay] Disconnected Event detected: %s",
|
||||||
netEvent.peer == m_server ? "same client" : "diff client");
|
netEvent.peer == m_server ? "same client" : "diff client");
|
||||||
|
|
||||||
// If the disconnect event doesn't come from the client we are actually listening to,
|
// If the disconnect event doesn't come from the client we are actually listening to,
|
||||||
// it can be safely ignored
|
// it can be safely ignored
|
||||||
if (netEvent.peer == m_server)
|
if (netEvent.peer == m_server)
|
||||||
{
|
{
|
||||||
m_do_loop.Clear(); // Stop the loop, will trigger a disconnect
|
m_do_loop.Clear(); // Stop the loop, will trigger a disconnect
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -550,8 +562,10 @@ void SlippiNetplayClient::SendConnectionSelected()
|
||||||
void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
|
void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
|
||||||
{
|
{
|
||||||
auto status = slippiConnectStatus;
|
auto status = slippiConnectStatus;
|
||||||
bool connectionFailed = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED;
|
bool connectionFailed =
|
||||||
bool connectionDisconnected = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_DISCONNECTED;
|
status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED;
|
||||||
|
bool connectionDisconnected =
|
||||||
|
status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_DISCONNECTED;
|
||||||
if (connectionFailed || connectionDisconnected)
|
if (connectionFailed || connectionDisconnected)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -559,8 +573,9 @@ void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
|
||||||
|
|
||||||
// if (pad && isDecider)
|
// if (pad && isDecider)
|
||||||
//{
|
//{
|
||||||
// ERROR_LOG(SLIPPI_ONLINE, "[%d] %X %X %X %X %X %X %X %X", pad->frame, pad->padBuf[0], pad->padBuf[1],
|
// ERROR_LOG(SLIPPI_ONLINE, "[%d] %X %X %X %X %X %X %X %X", pad->frame, pad->padBuf[0],
|
||||||
// pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5], pad->padBuf[6], pad->padBuf[7]);
|
// pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5],
|
||||||
|
// pad->padBuf[6], pad->padBuf[7]);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
if (pad)
|
if (pad)
|
||||||
|
@ -590,10 +605,10 @@ void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
|
||||||
INFO_LOG(SLIPPI_ONLINE, "Sending a packet of inputs [%d]...", frame);
|
INFO_LOG(SLIPPI_ONLINE, "Sending a packet of inputs [%d]...", frame);
|
||||||
for (auto it = localPadQueue.begin(); it != localPadQueue.end(); ++it)
|
for (auto it = localPadQueue.begin(); it != localPadQueue.end(); ++it)
|
||||||
{
|
{
|
||||||
INFO_LOG(SLIPPI_ONLINE, "Send [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", (*it)->frame, (*it)->padBuf[0],
|
INFO_LOG(SLIPPI_ONLINE, "Send [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", (*it)->frame,
|
||||||
(*it)->padBuf[1], (*it)->padBuf[2], (*it)->padBuf[3], (*it)->padBuf[4], (*it)->padBuf[5],
|
(*it)->padBuf[0], (*it)->padBuf[1], (*it)->padBuf[2], (*it)->padBuf[3],
|
||||||
(*it)->padBuf[6], (*it)->padBuf[7]);
|
(*it)->padBuf[4], (*it)->padBuf[5], (*it)->padBuf[6], (*it)->padBuf[7]);
|
||||||
spac->append((*it)->padBuf, SLIPPI_PAD_DATA_SIZE); // only transfer 8 bytes per pad
|
spac->append((*it)->padBuf, SLIPPI_PAD_DATA_SIZE); // only transfer 8 bytes per pad
|
||||||
}
|
}
|
||||||
|
|
||||||
SendAsync(std::move(spac));
|
SendAsync(std::move(spac));
|
||||||
|
@ -626,7 +641,7 @@ void SlippiNetplayClient::SetMatchSelections(SlippiPlayerSelections& s)
|
||||||
|
|
||||||
std::unique_ptr<SlippiRemotePadOutput> SlippiNetplayClient::GetSlippiRemotePad(int32_t curFrame)
|
std::unique_ptr<SlippiRemotePadOutput> SlippiNetplayClient::GetSlippiRemotePad(int32_t curFrame)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(pad_mutex); // TODO: Is this the correct lock?
|
std::lock_guard<std::mutex> lk(pad_mutex); // TODO: Is this the correct lock?
|
||||||
|
|
||||||
std::unique_ptr<SlippiRemotePadOutput> padOutput = std::make_unique<SlippiRemotePadOutput>();
|
std::unique_ptr<SlippiRemotePadOutput> padOutput = std::make_unique<SlippiRemotePadOutput>();
|
||||||
|
|
||||||
|
@ -677,7 +692,7 @@ std::string SlippiNetplayClient::GetOpponentName()
|
||||||
|
|
||||||
int32_t SlippiNetplayClient::GetSlippiLatestRemoteFrame()
|
int32_t SlippiNetplayClient::GetSlippiLatestRemoteFrame()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(pad_mutex); // TODO: Is this the correct lock?
|
std::lock_guard<std::mutex> lk(pad_mutex); // TODO: Is this the correct lock?
|
||||||
|
|
||||||
if (remotePadQueue.empty())
|
if (remotePadQueue.empty())
|
||||||
{
|
{
|
||||||
|
@ -713,7 +728,7 @@ s32 SlippiNetplayClient::CalcTimeOffsetUs()
|
||||||
int count = end - offset;
|
int count = end - offset;
|
||||||
if (count <= 0)
|
if (count <= 0)
|
||||||
{
|
{
|
||||||
return 0; // What do I return here?
|
return 0; // What do I return here?
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum / count;
|
return sum / count;
|
||||||
|
|
|
@ -4,6 +4,16 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <SFML/Network/Packet.hpp>
|
||||||
|
#include <array>
|
||||||
|
#include <deque>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/Event.h"
|
#include "Common/Event.h"
|
||||||
#include "Common/Timer.h"
|
#include "Common/Timer.h"
|
||||||
|
@ -11,22 +21,13 @@
|
||||||
#include "Core/NetPlayProto.h"
|
#include "Core/NetPlayProto.h"
|
||||||
#include "Core/Slippi/SlippiPad.h"
|
#include "Core/Slippi/SlippiPad.h"
|
||||||
#include "InputCommon/GCPadStatus.h"
|
#include "InputCommon/GCPadStatus.h"
|
||||||
#include <SFML/Network/Packet.hpp>
|
|
||||||
#include <array>
|
|
||||||
#include <deque>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Qos2.h>
|
#include <Qos2.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SLIPPI_ONLINE_LOCKSTEP_INTERVAL 30 // Number of frames to wait before attempting to time-sync
|
#define SLIPPI_ONLINE_LOCKSTEP_INTERVAL \
|
||||||
|
30 // Number of frames to wait before attempting to time-sync
|
||||||
#define SLIPPI_PING_DISPLAY_INTERVAL 60
|
#define SLIPPI_PING_DISPLAY_INTERVAL 60
|
||||||
|
|
||||||
struct SlippiRemotePadOutput
|
struct SlippiRemotePadOutput
|
||||||
|
@ -103,8 +104,9 @@ public:
|
||||||
void ThreadFunc();
|
void ThreadFunc();
|
||||||
void SendAsync(std::unique_ptr<sf::Packet> packet);
|
void SendAsync(std::unique_ptr<sf::Packet> packet);
|
||||||
|
|
||||||
SlippiNetplayClient(bool isDecider); // Make a dummy client
|
SlippiNetplayClient(bool isDecider); // Make a dummy client
|
||||||
SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort, bool isDecider);
|
SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort,
|
||||||
|
bool isDecider);
|
||||||
~SlippiNetplayClient();
|
~SlippiNetplayClient();
|
||||||
|
|
||||||
// Slippi Online
|
// Slippi Online
|
||||||
|
@ -149,8 +151,8 @@ protected:
|
||||||
std::thread m_thread;
|
std::thread m_thread;
|
||||||
|
|
||||||
std::string m_selected_game;
|
std::string m_selected_game;
|
||||||
Common::Flag m_is_running{ false };
|
Common::Flag m_is_running{false};
|
||||||
Common::Flag m_do_loop{ true };
|
Common::Flag m_do_loop{true};
|
||||||
|
|
||||||
unsigned int m_minimum_buffer_size = 6;
|
unsigned int m_minimum_buffer_size = 6;
|
||||||
|
|
||||||
|
@ -176,8 +178,8 @@ protected:
|
||||||
bool hasGameStarted = false;
|
bool hasGameStarted = false;
|
||||||
FrameTiming lastFrameTiming;
|
FrameTiming lastFrameTiming;
|
||||||
u64 pingUs;
|
u64 pingUs;
|
||||||
std::deque<std::unique_ptr<SlippiPad>> localPadQueue; // most recent inputs at start of deque
|
std::deque<std::unique_ptr<SlippiPad>> localPadQueue; // most recent inputs at start of deque
|
||||||
std::deque<std::unique_ptr<SlippiPad>> remotePadQueue; // most recent inputs at start of deque
|
std::deque<std::unique_ptr<SlippiPad>> remotePadQueue; // most recent inputs at start of deque
|
||||||
std::queue<FrameTiming> ackTimers;
|
std::queue<FrameTiming> ackTimers;
|
||||||
SlippiConnectStatus slippiConnectStatus = SlippiConnectStatus::NET_CONNECT_STATUS_UNSET;
|
SlippiConnectStatus slippiConnectStatus = SlippiConnectStatus::NET_CONNECT_STATUS_UNSET;
|
||||||
SlippiMatchInfo matchInfo;
|
SlippiMatchInfo matchInfo;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// TODO: Confirm the default and padding values are right
|
// TODO: Confirm the default and padding values are right
|
||||||
static u8 emptyPad[SLIPPI_PAD_FULL_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
static u8 emptyPad[SLIPPI_PAD_FULL_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
|
||||||
SlippiPad::SlippiPad(int32_t frame)
|
SlippiPad::SlippiPad(int32_t frame)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,4 +15,3 @@ public:
|
||||||
int32_t frame;
|
int32_t frame;
|
||||||
u8 padBuf[SLIPPI_PAD_FULL_SIZE];
|
u8 padBuf[SLIPPI_PAD_FULL_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -86,8 +86,7 @@ void SlippiPlaybackStatus::prepareSlippiPlayback(s32& frameIndex)
|
||||||
// TODO: figure out why sometimes playback frame increments past targetFrameNum
|
// TODO: figure out why sometimes playback frame increments past targetFrameNum
|
||||||
if (inSlippiPlayback && frameIndex >= targetFrameNum)
|
if (inSlippiPlayback && frameIndex >= targetFrameNum)
|
||||||
{
|
{
|
||||||
INFO_LOG(SLIPPI, "Reached frame %d. Target was %d. Unblocking", frameIndex,
|
INFO_LOG(SLIPPI, "Reached frame %d. Target was %d. Unblocking", frameIndex, targetFrameNum);
|
||||||
targetFrameNum);
|
|
||||||
cv_waitingForTargetFrame.notify_one();
|
cv_waitingForTargetFrame.notify_one();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +100,7 @@ void SlippiPlaybackStatus::resetPlayback()
|
||||||
if (m_savestateThread.joinable())
|
if (m_savestateThread.joinable())
|
||||||
m_savestateThread.detach();
|
m_savestateThread.detach();
|
||||||
|
|
||||||
condVar.notify_one(); // Will allow thread to kill itself
|
condVar.notify_one(); // Will allow thread to kill itself
|
||||||
futureDiffs.clear();
|
futureDiffs.clear();
|
||||||
futureDiffs.rehash(0);
|
futureDiffs.rehash(0);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +135,8 @@ void SlippiPlaybackStatus::SavestateThread()
|
||||||
{
|
{
|
||||||
// Wait to hit one of the intervals
|
// Wait to hit one of the intervals
|
||||||
// Possible while rewinding that we hit this wait again.
|
// Possible while rewinding that we hit this wait again.
|
||||||
while (shouldRunThreads && (currentPlaybackFrame - Slippi::PLAYBACK_FIRST_SAVE) % FRAME_INTERVAL != 0)
|
while (shouldRunThreads &&
|
||||||
|
(currentPlaybackFrame - Slippi::PLAYBACK_FIRST_SAVE) % FRAME_INTERVAL != 0)
|
||||||
condVar.wait(intervalLock);
|
condVar.wait(intervalLock);
|
||||||
|
|
||||||
if (!shouldRunThreads)
|
if (!shouldRunThreads)
|
||||||
|
@ -169,11 +169,13 @@ void SlippiPlaybackStatus::SavestateThread()
|
||||||
|
|
||||||
void SlippiPlaybackStatus::seekToFrame()
|
void SlippiPlaybackStatus::seekToFrame()
|
||||||
{
|
{
|
||||||
if (seekMtx.try_lock()) {
|
if (seekMtx.try_lock())
|
||||||
|
{
|
||||||
if (targetFrameNum < Slippi::PLAYBACK_FIRST_SAVE)
|
if (targetFrameNum < Slippi::PLAYBACK_FIRST_SAVE)
|
||||||
targetFrameNum = Slippi::PLAYBACK_FIRST_SAVE;
|
targetFrameNum = Slippi::PLAYBACK_FIRST_SAVE;
|
||||||
|
|
||||||
if (targetFrameNum > lastFrame) {
|
if (targetFrameNum > lastFrame)
|
||||||
|
{
|
||||||
targetFrameNum = lastFrame;
|
targetFrameNum = lastFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,8 +188,10 @@ void SlippiPlaybackStatus::seekToFrame()
|
||||||
if (prevState != Core::State::Paused)
|
if (prevState != Core::State::Paused)
|
||||||
Core::SetState(Core::State::Paused);
|
Core::SetState(Core::State::Paused);
|
||||||
|
|
||||||
s32 closestStateFrame = targetFrameNum - emod(targetFrameNum - Slippi::PLAYBACK_FIRST_SAVE, FRAME_INTERVAL);
|
s32 closestStateFrame =
|
||||||
bool isLoadingStateOptimal = targetFrameNum < currentPlaybackFrame || closestStateFrame > currentPlaybackFrame;
|
targetFrameNum - emod(targetFrameNum - Slippi::PLAYBACK_FIRST_SAVE, FRAME_INTERVAL);
|
||||||
|
bool isLoadingStateOptimal =
|
||||||
|
targetFrameNum < currentPlaybackFrame || closestStateFrame > currentPlaybackFrame;
|
||||||
|
|
||||||
if (isLoadingStateOptimal)
|
if (isLoadingStateOptimal)
|
||||||
{
|
{
|
||||||
|
@ -206,7 +210,7 @@ void SlippiPlaybackStatus::seekToFrame()
|
||||||
{
|
{
|
||||||
s32 closestActualStateFrame = closestStateFrame - FRAME_INTERVAL;
|
s32 closestActualStateFrame = closestStateFrame - FRAME_INTERVAL;
|
||||||
while (closestActualStateFrame > Slippi::PLAYBACK_FIRST_SAVE &&
|
while (closestActualStateFrame > Slippi::PLAYBACK_FIRST_SAVE &&
|
||||||
futureDiffs.count(closestActualStateFrame) == 0)
|
futureDiffs.count(closestActualStateFrame) == 0)
|
||||||
closestActualStateFrame -= FRAME_INTERVAL;
|
closestActualStateFrame -= FRAME_INTERVAL;
|
||||||
loadState(closestActualStateFrame);
|
loadState(closestActualStateFrame);
|
||||||
}
|
}
|
||||||
|
@ -214,10 +218,11 @@ void SlippiPlaybackStatus::seekToFrame()
|
||||||
{
|
{
|
||||||
s32 closestActualStateFrame = closestStateFrame - FRAME_INTERVAL;
|
s32 closestActualStateFrame = closestStateFrame - FRAME_INTERVAL;
|
||||||
while (closestActualStateFrame > currentPlaybackFrame &&
|
while (closestActualStateFrame > currentPlaybackFrame &&
|
||||||
futureDiffs.count(closestActualStateFrame) == 0)
|
futureDiffs.count(closestActualStateFrame) == 0)
|
||||||
closestActualStateFrame -= FRAME_INTERVAL;
|
closestActualStateFrame -= FRAME_INTERVAL;
|
||||||
|
|
||||||
// only load a savestate if we find one past our current frame since we are seeking forwards
|
// only load a savestate if we find one past our current frame since we are seeking
|
||||||
|
// forwards
|
||||||
if (closestActualStateFrame > currentPlaybackFrame)
|
if (closestActualStateFrame > currentPlaybackFrame)
|
||||||
loadState(closestActualStateFrame);
|
loadState(closestActualStateFrame);
|
||||||
}
|
}
|
||||||
|
@ -234,18 +239,22 @@ void SlippiPlaybackStatus::seekToFrame()
|
||||||
setHardFFW(false);
|
setHardFFW(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We've reached the frame we want. Reset targetFrameNum and release mutex so another seek can be performed
|
// We've reached the frame we want. Reset targetFrameNum and release mutex so another seek can
|
||||||
|
// be performed
|
||||||
g_playbackStatus->currentPlaybackFrame = targetFrameNum;
|
g_playbackStatus->currentPlaybackFrame = targetFrameNum;
|
||||||
targetFrameNum = INT_MAX;
|
targetFrameNum = INT_MAX;
|
||||||
Core::SetState(prevState);
|
Core::SetState(prevState);
|
||||||
seekMtx.unlock();
|
seekMtx.unlock();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
INFO_LOG(SLIPPI, "Already seeking. Ignoring this call");
|
INFO_LOG(SLIPPI, "Already seeking. Ignoring this call");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set isHardFFW and update OC settings to speed up the FFW
|
// Set isHardFFW and update OC settings to speed up the FFW
|
||||||
void SlippiPlaybackStatus::setHardFFW(bool enable) {
|
void SlippiPlaybackStatus::setHardFFW(bool enable)
|
||||||
|
{
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
SConfig::GetInstance().m_OCEnable = true;
|
SConfig::GetInstance().m_OCEnable = true;
|
||||||
|
@ -260,7 +269,6 @@ void SlippiPlaybackStatus::setHardFFW(bool enable) {
|
||||||
isHardFFW = enable;
|
isHardFFW = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SlippiPlaybackStatus::loadState(s32 closestStateFrame)
|
void SlippiPlaybackStatus::loadState(s32 closestStateFrame)
|
||||||
{
|
{
|
||||||
if (closestStateFrame == Slippi::PLAYBACK_FIRST_SAVE)
|
if (closestStateFrame == Slippi::PLAYBACK_FIRST_SAVE)
|
||||||
|
@ -268,7 +276,8 @@ void SlippiPlaybackStatus::loadState(s32 closestStateFrame)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::string stateString;
|
std::string stateString;
|
||||||
decoder.Decode((char*)iState.data(), iState.size(), futureDiffs[closestStateFrame].get(), &stateString);
|
decoder.Decode((char*)iState.data(), iState.size(), futureDiffs[closestStateFrame].get(),
|
||||||
|
&stateString);
|
||||||
std::vector<u8> stateToLoad(stateString.begin(), stateString.end());
|
std::vector<u8> stateToLoad(stateString.begin(), stateString.end());
|
||||||
State::LoadFromBuffer(stateToLoad);
|
State::LoadFromBuffer(stateToLoad);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <SlippiLib/SlippiGame.h>
|
||||||
#include <open-vcdiff/src/google/vcdecoder.h>
|
#include <open-vcdiff/src/google/vcdecoder.h>
|
||||||
#include <open-vcdiff/src/google/vcencoder.h>
|
#include <open-vcdiff/src/google/vcencoder.h>
|
||||||
#include <SlippiLib/SlippiGame.h>
|
|
||||||
|
|
||||||
#include "Core/ConfigManager.h"
|
|
||||||
#include "../../Common/CommonTypes.h"
|
#include "../../Common/CommonTypes.h"
|
||||||
|
#include "Core/ConfigManager.h"
|
||||||
|
|
||||||
class SlippiPlaybackStatus
|
class SlippiPlaybackStatus
|
||||||
{
|
{
|
||||||
|
@ -48,9 +48,9 @@ private:
|
||||||
void updateWatchSettingsStartEnd();
|
void updateWatchSettingsStartEnd();
|
||||||
|
|
||||||
std::unordered_map<int32_t, std::shared_future<std::string>>
|
std::unordered_map<int32_t, std::shared_future<std::string>>
|
||||||
futureDiffs; // State diffs keyed by frameIndex, processed async
|
futureDiffs; // State diffs keyed by frameIndex, processed async
|
||||||
std::vector<u8> iState; // The initial state
|
std::vector<u8> iState; // The initial state
|
||||||
std::vector<u8> cState; // The current (latest) state
|
std::vector<u8> cState; // The current (latest) state
|
||||||
|
|
||||||
open_vcdiff::VCDiffDecoder decoder;
|
open_vcdiff::VCDiffDecoder decoder;
|
||||||
open_vcdiff::VCDiffEncoder* encoder = NULL;
|
open_vcdiff::VCDiffEncoder* encoder = NULL;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
#include "SlippiReplayComm.h"
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "SlippiReplayComm.h"
|
|
||||||
#include "Common/CommonPaths.h"
|
#include "Common/CommonPaths.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Common/Logging/LogManager.h"
|
#include "Common/Logging/LogManager.h"
|
||||||
|
@ -18,7 +18,8 @@ static inline void ltrim(std::string& s)
|
||||||
// trim from end (in place)
|
// trim from end (in place)
|
||||||
static inline void rtrim(std::string& s)
|
static inline void rtrim(std::string& s)
|
||||||
{
|
{
|
||||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end());
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(),
|
||||||
|
s.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
// trim from both ends (in place)
|
// trim from both ends (in place)
|
||||||
|
@ -31,11 +32,13 @@ static inline void trim(std::string& s)
|
||||||
SlippiReplayComm::SlippiReplayComm()
|
SlippiReplayComm::SlippiReplayComm()
|
||||||
{
|
{
|
||||||
INFO_LOG(EXPANSIONINTERFACE, "SlippiReplayComm: Using playback config path: %s",
|
INFO_LOG(EXPANSIONINTERFACE, "SlippiReplayComm: Using playback config path: %s",
|
||||||
SConfig::GetInstance().m_strSlippiInput.c_str());
|
SConfig::GetInstance().m_strSlippiInput.c_str());
|
||||||
configFilePath = SConfig::GetInstance().m_strSlippiInput.c_str();
|
configFilePath = SConfig::GetInstance().m_strSlippiInput.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
SlippiReplayComm::~SlippiReplayComm() {}
|
SlippiReplayComm::~SlippiReplayComm()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SlippiReplayComm::CommSettings SlippiReplayComm::getSettings()
|
SlippiReplayComm::CommSettings SlippiReplayComm::getSettings()
|
||||||
{
|
{
|
||||||
|
@ -177,8 +180,8 @@ void SlippiReplayComm::loadFile()
|
||||||
{
|
{
|
||||||
WARN_LOG(EXPANSIONINTERFACE, "Comm file load error detected. Check file format");
|
WARN_LOG(EXPANSIONINTERFACE, "Comm file load error detected. Check file format");
|
||||||
|
|
||||||
// Reset in the case of read error. this fixes a race condition where file mod time changes but
|
// Reset in the case of read error. this fixes a race condition where file mod time changes
|
||||||
// the file is not readable yet?
|
// but the file is not readable yet?
|
||||||
configLastLoadModTime = 0;
|
configLastLoadModTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <SlippiGame.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <SlippiGame.h>
|
|
||||||
|
|
||||||
#include <Common/CommonTypes.h>
|
#include <Common/CommonTypes.h>
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ public:
|
||||||
int endFrame = INT_MAX;
|
int endFrame = INT_MAX;
|
||||||
bool outputOverlayFiles;
|
bool outputOverlayFiles;
|
||||||
bool isRealTimeMode;
|
bool isRealTimeMode;
|
||||||
std::string rollbackDisplayMethod; // off, normal, visible
|
std::string rollbackDisplayMethod; // off, normal, visible
|
||||||
std::string commandId;
|
std::string commandId;
|
||||||
std::queue<WatchSettings> queue;
|
std::queue<WatchSettings> queue;
|
||||||
} CommSettings;
|
} CommSettings;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "SlippiSavestate.h"
|
#include "SlippiSavestate.h"
|
||||||
|
#include <vector>
|
||||||
#include "Common/CommonFuncs.h"
|
#include "Common/CommonFuncs.h"
|
||||||
#include "Common/MemoryUtil.h"
|
#include "Common/MemoryUtil.h"
|
||||||
#include "Core/HW/AudioInterface.h"
|
#include "Core/HW/AudioInterface.h"
|
||||||
|
@ -11,7 +12,6 @@
|
||||||
#include "Core/HW/ProcessorInterface.h"
|
#include "Core/HW/ProcessorInterface.h"
|
||||||
#include "Core/HW/SI/SI.h"
|
#include "Core/HW/SI/SI.h"
|
||||||
#include "Core/HW/VideoInterface.h"
|
#include "Core/HW/VideoInterface.h"
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
SlippiSavestate::SlippiSavestate()
|
SlippiSavestate::SlippiSavestate()
|
||||||
{
|
{
|
||||||
|
@ -47,52 +47,52 @@ bool cmpFn(SlippiSavestate::PreserveBlock pb1, SlippiSavestate::PreserveBlock pb
|
||||||
void SlippiSavestate::initBackupLocs()
|
void SlippiSavestate::initBackupLocs()
|
||||||
{
|
{
|
||||||
static std::vector<ssBackupLoc> fullBackupRegions = {
|
static std::vector<ssBackupLoc> fullBackupRegions = {
|
||||||
{0x80005520, 0x80005940, nullptr}, // Data Sections 0 and 1
|
{0x80005520, 0x80005940, nullptr}, // Data Sections 0 and 1
|
||||||
{0x803b7240, 0x804DEC00, nullptr}, // Data Sections 2-7 and in between sections including BSS
|
{0x803b7240, 0x804DEC00, nullptr}, // Data Sections 2-7 and in between sections including BSS
|
||||||
|
|
||||||
// Full Unknown Region: [804fec00 - 80BD5C40)
|
// Full Unknown Region: [804fec00 - 80BD5C40)
|
||||||
// https://docs.google.com/spreadsheets/d/16ccNK_qGrtPfx4U25w7OWIDMZ-NxN1WNBmyQhaDxnEg/edit?usp=sharing
|
// https://docs.google.com/spreadsheets/d/16ccNK_qGrtPfx4U25w7OWIDMZ-NxN1WNBmyQhaDxnEg/edit?usp=sharing
|
||||||
{0x8065c000, 0x8071b000, nullptr}, // Unknown Region Pt1
|
{0x8065c000, 0x8071b000, nullptr}, // Unknown Region Pt1
|
||||||
{0x80bb0000, 0x811AD5A0, nullptr}, // Unknown Region Pt2, Heap [80bd5c40 - 811AD5A0)
|
{0x80bb0000, 0x811AD5A0, nullptr}, // Unknown Region Pt2, Heap [80bd5c40 - 811AD5A0)
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<PreserveBlock> excludeSections = {
|
static std::vector<PreserveBlock> excludeSections = {
|
||||||
// Sound stuff
|
// Sound stuff
|
||||||
{0x804031A0, 0x24}, // [804031A0 - 804031C4)
|
{0x804031A0, 0x24}, // [804031A0 - 804031C4)
|
||||||
{0x80407FB4, 0x34C}, // [80407FB4 - 80408300)
|
{0x80407FB4, 0x34C}, // [80407FB4 - 80408300)
|
||||||
{0x80433C64, 0x1EE80}, // [80433C64 - 80452AE4)
|
{0x80433C64, 0x1EE80}, // [80433C64 - 80452AE4)
|
||||||
{0x804A8D78, 0x17A68}, // [804A8D78 - 804C07E0)
|
{0x804A8D78, 0x17A68}, // [804A8D78 - 804C07E0)
|
||||||
{0x804C28E0, 0x399C}, // [804C28E0 - 804C627C)
|
{0x804C28E0, 0x399C}, // [804C28E0 - 804C627C)
|
||||||
{0x804D7474, 0x8}, // [804D7474 - 804D747C)
|
{0x804D7474, 0x8}, // [804D7474 - 804D747C)
|
||||||
{0x804D74F0, 0x50}, // [804D74F0 - 804D7540)
|
{0x804D74F0, 0x50}, // [804D74F0 - 804D7540)
|
||||||
{0x804D7548, 0x4}, // [804D7548 - 804D754C)
|
{0x804D7548, 0x4}, // [804D7548 - 804D754C)
|
||||||
{0x804D7558, 0x24}, // [804D7558 - 804D757C)
|
{0x804D7558, 0x24}, // [804D7558 - 804D757C)
|
||||||
{0x804D7580, 0xC}, // [804D7580 - 804D758C)
|
{0x804D7580, 0xC}, // [804D7580 - 804D758C)
|
||||||
{0x804D759C, 0x4}, // [804D759C - 804D75A0)
|
{0x804D759C, 0x4}, // [804D759C - 804D75A0)
|
||||||
{0x804D7720, 0x4}, // [804D7720 - 804D7724)
|
{0x804D7720, 0x4}, // [804D7720 - 804D7724)
|
||||||
{0x804D7744, 0x4}, // [804D7744 - 804D7748)
|
{0x804D7744, 0x4}, // [804D7744 - 804D7748)
|
||||||
{0x804D774C, 0x8}, // [804D774C - 804D7754)
|
{0x804D774C, 0x8}, // [804D774C - 804D7754)
|
||||||
{0x804D7758, 0x8}, // [804D7758 - 804D7760)
|
{0x804D7758, 0x8}, // [804D7758 - 804D7760)
|
||||||
{0x804D7788, 0x10}, // [804D7788 - 804D7798)
|
{0x804D7788, 0x10}, // [804D7788 - 804D7798)
|
||||||
{0x804D77C8, 0x4}, // [804D77C8 - 804D77CC)
|
{0x804D77C8, 0x4}, // [804D77C8 - 804D77CC)
|
||||||
{0x804D77D0, 0x4}, // [804D77D0 - 804D77D4)
|
{0x804D77D0, 0x4}, // [804D77D0 - 804D77D4)
|
||||||
{0x804D77E0, 0x4}, // [804D77E0 - 804D77E4)
|
{0x804D77E0, 0x4}, // [804D77E0 - 804D77E4)
|
||||||
{0x804DE358, 0x80}, // [804DE358 - 804DE3D8)
|
{0x804DE358, 0x80}, // [804DE358 - 804DE3D8)
|
||||||
{0x804DE800, 0x70}, // [804DE800 - 804DE870)
|
{0x804DE800, 0x70}, // [804DE800 - 804DE870)
|
||||||
|
|
||||||
// The following need to be added to the ranges proper
|
// The following need to be added to the ranges proper
|
||||||
{0x804d6030, 0x4}, // ???
|
{0x804d6030, 0x4}, // ???
|
||||||
{0x804d603c, 0x4}, // ???
|
{0x804d603c, 0x4}, // ???
|
||||||
{0x804d7218, 0x4}, // ???
|
{0x804d7218, 0x4}, // ???
|
||||||
{0x804d7228, 0x8}, // ???
|
{0x804d7228, 0x8}, // ???
|
||||||
{0x804d7740, 0x4}, // ???
|
{0x804d7740, 0x4}, // ???
|
||||||
{0x804d7754, 0x4}, // ???
|
{0x804d7754, 0x4}, // ???
|
||||||
{0x804d77bc, 0x4}, // ???
|
{0x804d77bc, 0x4}, // ???
|
||||||
{0x804de7f0, 0x10}, // ???
|
{0x804de7f0, 0x10}, // ???
|
||||||
|
|
||||||
// Camera Blocks, Temporarily added here
|
// Camera Blocks, Temporarily added here
|
||||||
//{0x80452c7c, 0x2B0}, // Cam Block 1, including gaps
|
//{0x80452c7c, 0x2B0}, // Cam Block 1, including gaps
|
||||||
//{0x806e516c, 0xA8}, // Cam Block 2, including gaps
|
//{0x806e516c, 0xA8}, // Cam Block 2, including gaps
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<ssBackupLoc> processedLocs = {};
|
static std::vector<ssBackupLoc> processedLocs = {};
|
||||||
|
@ -146,7 +146,7 @@ void SlippiSavestate::initBackupLocs()
|
||||||
// Add split section after exclusion
|
// Add split section after exclusion
|
||||||
if (backupLocs[idx].endAddress > ipb.address + ipb.length)
|
if (backupLocs[idx].endAddress > ipb.address + ipb.length)
|
||||||
{
|
{
|
||||||
ssBackupLoc newLoc = { ipb.address + ipb.length, backupLocs[idx].endAddress, nullptr };
|
ssBackupLoc newLoc = {ipb.address + ipb.length, backupLocs[idx].endAddress, nullptr};
|
||||||
backupLocs.insert(backupLocs.begin() + idx + 1, newLoc);
|
backupLocs.insert(backupLocs.begin() + idx + 1, newLoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
|
||||||
|
@ -14,7 +14,10 @@ public:
|
||||||
u32 address;
|
u32 address;
|
||||||
u32 length;
|
u32 length;
|
||||||
|
|
||||||
bool operator==(const PreserveBlock& p) const { return address == p.address && length == p.length; }
|
bool operator==(const PreserveBlock& p) const
|
||||||
|
{
|
||||||
|
return address == p.address && length == p.length;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SlippiSavestate();
|
SlippiSavestate();
|
||||||
|
@ -46,7 +49,7 @@ private:
|
||||||
{
|
{
|
||||||
std::size_t operator()(const PreserveBlock& node) const
|
std::size_t operator()(const PreserveBlock& node) const
|
||||||
{
|
{
|
||||||
return node.address ^ node.length; // TODO: This is probably a bad hash
|
return node.address ^ node.length; // TODO: This is probably a bad hash
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ static void system_hidden(const char* cmd)
|
||||||
memset(&p_info, 0, sizeof(p_info));
|
memset(&p_info, 0, sizeof(p_info));
|
||||||
s_info.cb = sizeof(s_info);
|
s_info.cb = sizeof(s_info);
|
||||||
|
|
||||||
wchar_t utf16cmd[MAX_SYSTEM_PROGRAM] = { 0 };
|
wchar_t utf16cmd[MAX_SYSTEM_PROGRAM] = {0};
|
||||||
MultiByteToWideChar(CP_UTF8, 0, cmd, -1, utf16cmd, MAX_SYSTEM_PROGRAM);
|
MultiByteToWideChar(CP_UTF8, 0, cmd, -1, utf16cmd, MAX_SYSTEM_PROGRAM);
|
||||||
if (CreateProcessW(NULL, utf16cmd, NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &s_info, &p_info))
|
if (CreateProcessW(NULL, utf16cmd, NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &s_info, &p_info))
|
||||||
{
|
{
|
||||||
|
@ -110,19 +110,21 @@ bool SlippiUser::AttemptLogin()
|
||||||
|
|
||||||
{
|
{
|
||||||
std::string userFilePathTxt =
|
std::string userFilePathTxt =
|
||||||
userFilePath + ".txt"; // Put the filename here in its own scope because we don't really need it elsewhere
|
userFilePath +
|
||||||
|
".txt"; // Put the filename here in its own scope because we don't really need it elsewhere
|
||||||
if (File::Exists(userFilePathTxt))
|
if (File::Exists(userFilePathTxt))
|
||||||
{
|
{
|
||||||
// If both files exist we just log they exist and take no further action
|
// If both files exist we just log they exist and take no further action
|
||||||
if (File::Exists(userFilePath))
|
if (File::Exists(userFilePath))
|
||||||
{
|
{
|
||||||
INFO_LOG(SLIPPI_ONLINE,
|
INFO_LOG(SLIPPI_ONLINE, "Found both .json.txt and .json file for user data. Using .json "
|
||||||
"Found both .json.txt and .json file for user data. Using .json and ignoring the .json.txt");
|
"and ignoring the .json.txt");
|
||||||
}
|
}
|
||||||
// If only the .txt file exists move the contents to a json file and log if it fails
|
// If only the .txt file exists move the contents to a json file and log if it fails
|
||||||
else if (!File::Rename(userFilePathTxt, userFilePath))
|
else if (!File::Rename(userFilePathTxt, userFilePath))
|
||||||
{
|
{
|
||||||
WARN_LOG(SLIPPI_ONLINE, "Could not move file %s to %s", userFilePathTxt.c_str(), userFilePath.c_str());
|
WARN_LOG(SLIPPI_ONLINE, "Could not move file %s to %s", userFilePathTxt.c_str(),
|
||||||
|
userFilePath.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +139,8 @@ bool SlippiUser::AttemptLogin()
|
||||||
if (isLoggedIn)
|
if (isLoggedIn)
|
||||||
{
|
{
|
||||||
overwriteFromServer();
|
overwriteFromServer();
|
||||||
WARN_LOG(SLIPPI_ONLINE, "Found user %s (%s)", userInfo.displayName.c_str(), userInfo.uid.c_str());
|
WARN_LOG(SLIPPI_ONLINE, "Found user %s (%s)", userInfo.displayName.c_str(),
|
||||||
|
userInfo.uid.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return isLoggedIn;
|
return isLoggedIn;
|
||||||
|
@ -149,7 +152,8 @@ void SlippiUser::OpenLogInPage()
|
||||||
std::string path = getUserFilePath();
|
std::string path = getUserFilePath();
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// On windows, sometimes the path can have backslashes and slashes mixed, convert all to backslashes
|
// On windows, sometimes the path can have backslashes and slashes mixed, convert all to
|
||||||
|
// backslashes
|
||||||
path = ReplaceAll(path, "\\", "\\");
|
path = ReplaceAll(path, "\\", "\\");
|
||||||
path = ReplaceAll(path, "/", "\\");
|
path = ReplaceAll(path, "/", "\\");
|
||||||
#endif
|
#endif
|
||||||
|
@ -169,7 +173,7 @@ void SlippiUser::OpenLogInPage()
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
std::string command = "open \"" + fullUrl + "\"";
|
std::string command = "open \"" + fullUrl + "\"";
|
||||||
#else
|
#else
|
||||||
std::string command = "xdg-open \"" + fullUrl + "\""; // Linux
|
std::string command = "xdg-open \"" + fullUrl + "\""; // Linux
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RunSystemCommand(command);
|
RunSystemCommand(command);
|
||||||
|
@ -181,12 +185,15 @@ void SlippiUser::UpdateApp()
|
||||||
auto isoPath = SConfig::GetInstance().m_strIsoPath;
|
auto isoPath = SConfig::GetInstance().m_strIsoPath;
|
||||||
|
|
||||||
std::string path = File::GetExeDirectory() + "/dolphin-slippi-tools.exe";
|
std::string path = File::GetExeDirectory() + "/dolphin-slippi-tools.exe";
|
||||||
std::string echoMsg = "echo Starting update process. If nothing happen after a few "
|
std::string echoMsg =
|
||||||
"minutes, you may need to update manually from https://slippi.gg/netplay ...";
|
"echo Starting update process. If nothing happen after a few "
|
||||||
|
"minutes, you may need to update manually from https://slippi.gg/netplay ...";
|
||||||
// std::string command =
|
// std::string command =
|
||||||
// "start /b cmd /c " + echoMsg + " && \"" + path + "\" app-update -launch -iso \"" + isoPath + "\"";
|
// "start /b cmd /c " + echoMsg + " && \"" + path + "\" app-update -launch -iso \"" + isoPath +
|
||||||
std::string command = "start /b cmd /c " + echoMsg + " && \"" + path + "\" app-update -launch -iso \"" + isoPath +
|
// "\"";
|
||||||
"\" -version \"" + Common::scm_slippi_semver_str + "\"";
|
std::string command = "start /b cmd /c " + echoMsg + " && \"" + path +
|
||||||
|
"\" app-update -launch -iso \"" + isoPath + "\" -version \"" +
|
||||||
|
Common::scm_slippi_semver_str + "\"";
|
||||||
WARN_LOG(SLIPPI, "Executing app update command: %s", command.c_str());
|
WARN_LOG(SLIPPI, "Executing app update command: %s", command.c_str());
|
||||||
RunSystemCommand(command);
|
RunSystemCommand(command);
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
|
@ -263,7 +270,8 @@ void SlippiUser::FileListenThread()
|
||||||
std::string SlippiUser::getUserFilePath()
|
std::string SlippiUser::getUserFilePath()
|
||||||
{
|
{
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
std::string userFilePath = File::GetBundleDirectory() + "/Contents/Resources" + DIR_SEP + "user.json";
|
std::string userFilePath =
|
||||||
|
File::GetBundleDirectory() + "/Contents/Resources" + DIR_SEP + "user.json";
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
std::string userFilePath = File::GetExeDirectory() + DIR_SEP + "user.json";
|
std::string userFilePath = File::GetExeDirectory() + DIR_SEP + "user.json";
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
class SlippiUser
|
class SlippiUser
|
||||||
{
|
{
|
||||||
|
|
|
@ -94,22 +94,23 @@ void SlippiPane::CreateLayout()
|
||||||
connect(delay_spin, qOverload<int>(&QSpinBox::valueChanged), this,
|
connect(delay_spin, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||||
[](int delay) { SConfig::GetInstance().m_slippiOnlineDelay = delay; });
|
[](int delay) { SConfig::GetInstance().m_slippiOnlineDelay = delay; });
|
||||||
#else
|
#else
|
||||||
//Playback Settings
|
// Playback Settings
|
||||||
auto* playback_settings = new QGroupBox(tr("Playback Settings"));
|
auto* playback_settings = new QGroupBox(tr("Playback Settings"));
|
||||||
auto* playback_settings_layout = new QVBoxLayout();
|
auto* playback_settings_layout = new QVBoxLayout();
|
||||||
playback_settings->setLayout(playback_settings_layout);
|
playback_settings->setLayout(playback_settings_layout);
|
||||||
layout->addWidget(playback_settings);
|
layout->addWidget(playback_settings);
|
||||||
|
|
||||||
auto* enable_playback_seek_checkbox = new QCheckBox(tr("Enable Seekbar"));
|
auto* enable_playback_seek_checkbox = new QCheckBox(tr("Enable Seekbar"));
|
||||||
char seekbarTooltip[] = "<html><head/><body><p>Enables video player style controls while watching Slippi replays. Uses more cpu resources and can be stuttery. " \
|
char seekbarTooltip[] = "<html><head/><body><p>Enables video player style controls while "
|
||||||
"Space: Pause/Play " \
|
"watching Slippi replays. Uses more cpu resources and can be stuttery. "
|
||||||
"Left/Right: Jump 5 seconds back/forward" \
|
"Space: Pause/Play "
|
||||||
"Shift + Left/Right: Jump 20 seconds back/forward" \
|
"Left/Right: Jump 5 seconds back/forward"
|
||||||
"Period (while paused): Advance one frame";
|
"Shift + Left/Right: Jump 20 seconds back/forward"
|
||||||
|
"Period (while paused): Advance one frame";
|
||||||
enable_playback_seek_checkbox->setToolTip(tr(seekbarTooltip));
|
enable_playback_seek_checkbox->setToolTip(tr(seekbarTooltip));
|
||||||
playback_settings_layout->addWidget(enable_playback_seek_checkbox);
|
playback_settings_layout->addWidget(enable_playback_seek_checkbox);
|
||||||
enable_playback_seek_checkbox->setChecked(SConfig::GetInstance().m_slippiEnableSeek);
|
enable_playback_seek_checkbox->setChecked(SConfig::GetInstance().m_slippiEnableSeek);
|
||||||
connect(enable_playback_seek_checkbox, &QCheckBox::toggled, this,
|
connect(enable_playback_seek_checkbox, &QCheckBox::toggled, this,
|
||||||
[](bool checked) { SConfig::GetInstance().m_slippiEnableSeek = checked; });
|
[](bool checked) { SConfig::GetInstance().m_slippiEnableSeek = checked; });
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue