This commit is contained in:
Logan McNaughton 2020-04-21 09:09:50 -06:00
parent b7b56fea51
commit 1fccc3ba6c
19 changed files with 1173 additions and 50 deletions

View file

@ -8,7 +8,9 @@ addons:
apt:
packages:
- libsdl1.2-dev
- libsdl-net1.2-dev
- libsdl2-dev
- libsdl2-net-dev
- libfreetype6-dev
- libgl1-mesa-dev
- libglu1-mesa-dev
@ -30,6 +32,7 @@ env:
- DBG_COUNT=1
- DBG_COMPARE=1
- COUNT_INSTR=1
- NETPLAY=1
script:
- make -C projects/unix V=1 clean && LDFLAGS="-Wl,--no-add-needed -Wl,--no-undefined" OPTFLAGS="-O2" make SDL_CONFIG=sdl-config CC="${CC}" CXX="${CXX}" -j$(nproc) -C projects/unix V=1 all
- make -C projects/unix V=1 clean && LDFLAGS="-Wl,--no-add-needed -Wl,--no-undefined" OPTFLAGS="-O2" make SDL_CONFIG=sdl2-config CC="${CC}" CXX="${CXX}" -j$(nproc) -C projects/unix V=1 all
@ -46,6 +49,7 @@ matrix:
- sudo apt-get update -qq
- sudo apt-get -y --allow-unauthenticated install mxe-i686-w64-mingw32.shared-gcc
- sudo apt-get -y --allow-unauthenticated install mxe-i686-w64-mingw32.shared-sdl2
- sudo apt-get -y --allow-unauthenticated install mxe-i686-w64-mingw32.shared-sdl2-net
- sudo apt-get -y --allow-unauthenticated install mxe-i686-w64-mingw32.shared-freeglut
- sudo apt-get -y --allow-unauthenticated install mxe-i686-w64-mingw32.shared-freetype
- sudo apt-get -y --allow-unauthenticated install mxe-i686-w64-mingw32.shared-libpng
@ -66,6 +70,7 @@ matrix:
- sudo apt-get update -qq
- sudo apt-get -y --allow-unauthenticated install mxe-x86-64-w64-mingw32.shared-gcc
- sudo apt-get -y --allow-unauthenticated install mxe-x86-64-w64-mingw32.shared-sdl2
- sudo apt-get -y --allow-unauthenticated install mxe-x86-64-w64-mingw32.shared-sdl2-net
- sudo apt-get -y --allow-unauthenticated install mxe-x86-64-w64-mingw32.shared-freeglut
- sudo apt-get -y --allow-unauthenticated install mxe-x86-64-w64-mingw32.shared-freetype
- sudo apt-get -y --allow-unauthenticated install mxe-x86-64-w64-mingw32.shared-libpng

View file

@ -190,6 +190,26 @@ Most libmupen64plus functions return an <tt>m64p_error</tt> return code, which i
|This command allow frontends to register their media (such as GameBoy cartridge or 64DD disk) loading functions. These functions will be called appropriately by the core at startup and when a new media is inserted.
|'''<tt>ParamInt</tt>''' must be sizeof(m64p_media_loader).'''<br /><tt>ParamPtr</tt>'''A pointer to the m64p_media_loader to register, cannot be NULL.
|None
|-
|M64CMD_NETPLAY_INIT
|This command will initilize the netplay subsystem. It will also attempt to make an initial connection to the netplay server. Returns M64ERR_SYSTEM_FAIL on failure, M64ERR_SUCCESS on success.
|'''<tt>ParamInt</tt>''' This is the port number the netplay server is listening on.'''<br /><tt>ParamPtr</tt>''' This is a string containing the IP or hostname of the netplay server.
|Emulator should be stopped.
|-
|M64CMD_NETPLAY_CONTROL_PLAYER
|This function is used for the client to request control of certain players. This function can be called more than once if a client would like to control multiple players. Local controllers are assigned in order. Returns M64ERR_INPUT_ASSERT if the server rejects the request. If this function is never called, the client is assumed to be a spectator.
|'''<tt>ParamInt</tt>''' This is the player number (from 1-4) the client would like to control.'''<br /><tt>ParamPtr</tt>''' This is a pointer to a uint32_t that contains a unique registration ID (for instance a random number) for the client. This number should be the same each time if this function is called multiple times. The registration ID cannot be 0.
|Emulator should be stopped. The input plugin should be started.
|-
|M64CMD_NETPLAY_GET_VERSION
|This function is used to check API and core versions, the server can use this information to make sure everyone is using the same version of the software. The function returns M64ERR_INCOMPATIBLE if the API versions don't match, and M64ERR_SUCCESS if they do.
|'''<tt>ParamInt</tt>''' This is the Netplay API version that the frontend supports.'''<br /><tt>ParamPtr</tt>''' This is a pointer to a uint32_t, the core fills this pointer with a core netplay version. This version number is incremented when changes are made to the core that would affect netplay sync.
|None
|-
|M64CMD_NETPLAY_CLOSE
|Closes the netplay subsystem, as well as any connections to the netplay server.
|N/A
|None
|}
<br />

View file

@ -0,0 +1,113 @@
= Mupen64Plus Netplay API =
The Mupen64Plus netplay API is based on a client/server architecture. It supports syncing core settings and save files. It also supports raw input (tested with Raphnet input plugin).
TCP is used to deal with player registration, and syncing settings and saves.
UDP is used to transfer key input data.
Data is sent in Network Byte Order (Big Endian).
The server is responsible for maintaining healthy buffers and also for detecting desyncs. The server is the source of truth for input data (for instance, when player 1 pushes "A", it sends that information to the server via UDP. That "A" is not registered as input for player 1 until the client receives a packet from the server indicating what input frame to register that "A" in)
== UDP Packet formats ==
* Request input for a player (sent by client):
** 12 bytes
** byte[0] = 2
** byte[1] = player number (this is the player we need key inputs for)
** byte[2-5] = client registration ID
** byte[6-9] = current event count
** byte[10] = whether client is a spectator
** byte[11] = local buffer size of the client
* Key input data (sent by server):
** Variable length, payload cannot be larger than 512 bytes
** byte[0] = 1
** byte[1] = player number the key inputs pertain to
** byte[2] = current status. Bit 0 indicates whether the game has desynced (1 means desync). Bits 1-4 indicate whether a player has disconnected (for instance, if bit 2 is set, that means player 2 has disconnected). Bits 5-7 are currently unused.
** byte[3] = how far we lag behind the lead player
** byte[4] = number of events in this packet
** The following items will repeat/loop for the number of events in the packet
*** byte[5-8] = event count
*** byte[9-12] = key input data
*** byte[13] = current plugin
* Key input data (sent by client):
** 11 bytes
** byte[0] = 0
** byte[1] = player number
** byte[2-5] = current event count
** byte[6-9] = key input data
** byte[10] = current plugin
* Client sync data (sent by client):
** 133 bytes
** byte[0] = 4
** byte[1-4] = current VI count
** byte[5-132] = CP0 registers
== TCP Packet formats ==
* Player disconnection notice (sent by client):
** 5 bytes
** byte[0] = 7
** byte[1-4] = client registration ID
* Player registration request (sent by client):
** 8 bytes
** byte[0] = 5
** byte[1] = player the client is trying to register
** byte[2] = plugin for this player
** byte[3] = whether to use a raw input plugin
** byte[4-7] = client registration ID
* Player registration response (sent by server):
** 2 bytes
** byte[0] = response (1 if registration was successful, 0 otherwise)
** byte[1] = local buffer target for the client
* Send save file data (sent by client):
** Variable length
** byte[0] = 1
** byte[1 - next '\0'] = file name
** byte[4 bytes] = size of save file in bytes
** byte[size of file] = save file data
* Request save file data (sent by client):
** Variable length
** byte[0] = 2
** byte[1 - next '\0'] = file name
* Receive save file data (sent by server):
** Server responds with this data right after the above request
** Variable length
** byte[file size] = save file data
* Send settings (sent by client):
** 21 bytes
** byte[0] = 3
** byte[1-4] = count_per_op
** byte[5-8] = disable_extra_mem
** byte[9-12] = si_dma_duration
** byte[13-16] = emumode
** byte[17-20] = no_compiled_jump
* Request settings (sent by client):
** 1 byte
** byte[0] = 4
* Send settings (sent by server):
** Server responds with this data right after the above request
** 20 bytes
** byte[0-3] = count_per_op
** byte[4-7] = disable_extra_mem
** byte[8-11] = si_dma_duration
** byte[12-15] = emumode
** byte[16-19] = no_compiled_jump
* Request player registration data (sent by client):
** 1 byte
** byte[0] = 6
* Send player registration data (sent by server):
** 24 bytes
** The following bytes loop 4 times (once for each player)
*** byte[0-3] = player registration ID
*** byte[4] = player plugin
*** byte[5] = whether the player is using raw data input

View file

@ -149,6 +149,7 @@
<ClCompile Include="..\..\src\main\eventloop.c" />
<ClCompile Include="..\..\src\main\lirc.c" />
<ClCompile Include="..\..\src\main\main.c" />
<ClCompile Include="..\..\src\main\netplay.c" />
<ClCompile Include="..\..\src\main\rom.c" />
<ClCompile Include="..\..\src\main\savestates.c" />
<ClCompile Include="..\..\src\main\screenshot.c" />
@ -451,6 +452,7 @@
<ClInclude Include="..\..\src\main\lirc.h" />
<ClInclude Include="..\..\src\main\list.h" />
<ClInclude Include="..\..\src\main\main.h" />
<ClInclude Include="..\..\src\main\netplay.h" />
<ClInclude Include="..\..\src\main\rom.h" />
<ClInclude Include="..\..\src\main\savestates.h" />
<ClInclude Include="..\..\src\main\screenshot.h" />
@ -930,14 +932,14 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_PARALLEL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
@ -948,14 +950,14 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
@ -966,14 +968,14 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='New_Dynarec_Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_PARALLEL;NEW_DYNAREC=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;NEW_DYNAREC=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
@ -984,14 +986,14 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x86_New_Dynarec_Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_PARALLEL;NEW_DYNAREC=1;RECOMPILER_DEBUG=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;NEW_DYNAREC=1;RECOMPILER_DEBUG=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x86\capstone_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x86\capstone_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
@ -1002,14 +1004,14 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ARM_New_Dynarec_Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_PARALLEL;NEW_DYNAREC=1;RECOMPILER_DEBUG=3;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;NEW_DYNAREC=1;RECOMPILER_DEBUG=3;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x86\capstone_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x86\capstone_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
@ -1020,14 +1022,14 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='New_Dynarec_Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;NEW_DYNAREC=2;__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;NEW_DYNAREC=2;__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
@ -1038,14 +1040,14 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ARM64_New_Dynarec_Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;NEW_DYNAREC=2;__x86_64__;RECOMPILER_DEBUG=4;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;NEW_DYNAREC=2;__x86_64__;RECOMPILER_DEBUG=4;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x64\capstone_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x64\capstone_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
@ -1056,14 +1058,14 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64_New_Dynarec_Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;NEW_DYNAREC=2;__x86_64__;RECOMPILER_DEBUG=2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;NEW_DYNAREC=2;__x86_64__;RECOMPILER_DEBUG=2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x64\capstone_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x64\capstone_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
@ -1073,8 +1075,8 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_PARALLEL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
@ -1082,7 +1084,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
@ -1094,8 +1096,8 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
@ -1103,7 +1105,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
@ -1115,8 +1117,8 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='New_Dynarec_Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_PARALLEL;NEW_DYNAREC=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;NEW_DYNAREC=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
@ -1124,7 +1126,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
@ -1136,8 +1138,8 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='New_Dynarec_Release|x64'">
<ClCompile>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;NEW_DYNAREC=2;__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;NEW_DYNAREC=2;__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
@ -1145,7 +1147,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
@ -1158,4 +1160,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -153,6 +153,9 @@
<ClCompile Include="..\..\src\main\main.c">
<Filter>main</Filter>
</ClCompile>
<ClCompile Include="..\..\src\main\netplay.c">
<Filter>main</Filter>
</ClCompile>
<ClCompile Include="..\..\src\main\rom.c">
<Filter>main</Filter>
</ClCompile>
@ -464,6 +467,9 @@
<ClInclude Include="..\..\src\main\main.h">
<Filter>main</Filter>
</ClInclude>
<ClInclude Include="..\..\src\main\netplay.h">
<Filter>main</Filter>
</ClInclude>
<ClInclude Include="..\..\src\main\rom.h">
<Filter>main</Filter>
</ClInclude>

View file

@ -326,8 +326,15 @@ ifeq ($(origin SDL_CFLAGS) $(origin SDL_LDLIBS), undefined undefined)
ifeq ($(shell which $(SDL_CONFIG) 2>/dev/null),)
$(error No SDL development libraries found!)
else
ifeq ($(NETPLAY), 1)
SDL_LDLIBS += -lSDL_net
endif
$(warning Using SDL 1.2 libraries)
endif
else
ifeq ($(NETPLAY), 1)
SDL_LDLIBS += -lSDL2_net
endif
endif
SDL_CFLAGS += $(shell $(SDL_CONFIG) --cflags)
SDL_LDLIBS += $(shell $(SDL_CONFIG) --libs)
@ -581,6 +588,12 @@ SOURCE += $(SUBDIR)/oglft/OGLFT.cpp
CXXFLAGS += -I$(SUBDIR)/oglft
endif
# netplay
ifeq ($(NETPLAY), 1)
CFLAGS += -DM64P_NETPLAY
SOURCE += $(SRCDIR)/main/netplay.c
endif
# source files for optional features
ifeq ($(DBG_COUNT), 1)
CFLAGS += -DCOUNT_INSTR
@ -710,6 +723,7 @@ targets:
@echo " WARNFLAGS=flag == compiler warning levels (default: -Wall)"
@echo " PIC=(1|0) == Force enable/disable of position independent code"
@echo " OSD=(1|0) == Enable/disable build of OpenGL On-screen display"
@echo " NETPLAY=1 == Enable netplay functionality, requires SDL2_net
@echo " NEW_DYNAREC=1 == Replace dynamic recompiler with Ari64's experimental dynarec"
@echo " OPENCV=1 == Enable OpenCV support"
@echo " POSTFIX=name == String added to the name of the the build (default: '')"

View file

@ -27,6 +27,8 @@ ConfigSetDefaultInt;
ConfigSetDefaultString;
ConfigSetParameter;
ConfigSetParameterHelp;
ConfigSendNetplayConfig;
ConfigReceiveNetplayConfig;
CoreAddCheat;
CoreAttachPlugin;
CoreCheatEnabled;

View file

@ -34,6 +34,7 @@
#include "m64p_config.h"
#include "m64p_types.h"
#include "main/util.h"
#include "main/netplay.h"
#include "osal/files.h"
#include "osal/preproc.h"
@ -1595,3 +1596,12 @@ EXPORT const char * CALL ConfigGetUserCachePath(void)
return osal_get_user_cachepath();
}
EXPORT m64p_error CALL ConfigSendNetplayConfig(char* data, int size)
{
return netplay_send_config(data, size);
}
EXPORT m64p_error CALL ConfigReceiveNetplayConfig(char* data, int size)
{
return netplay_receive_config(data, size);
}

View file

@ -45,6 +45,7 @@
#include "main/version.h"
#include "main/workqueue.h"
#include "main/screenshot.h"
#include "main/netplay.h"
#include "plugin/plugin.h"
#include "vidext.h"
@ -309,6 +310,30 @@ EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *P
return M64ERR_INPUT_INVALID;
g_media_loader = *(m64p_media_loader*)ParamPtr;
return M64ERR_SUCCESS;
case M64CMD_NETPLAY_INIT:
if (ParamInt < 1 || ParamPtr == NULL)
return M64ERR_INPUT_INVALID;
return netplay_start(ParamPtr, ParamInt);
case M64CMD_NETPLAY_CONTROL_PLAYER:
if (ParamInt < 1 || ParamInt > 4 || ParamPtr == NULL)
return M64ERR_INPUT_INVALID;
if (netplay_register_player(ParamInt - 1, Controls[netplay_next_controller()].Plugin, Controls[netplay_next_controller()].RawData, *(uint32_t*)ParamPtr))
{
netplay_set_controller(ParamInt - 1);
return M64ERR_SUCCESS;
}
else
return M64ERR_INPUT_ASSERT; // player already in use
case M64CMD_NETPLAY_GET_VERSION:
if (ParamPtr == NULL)
return M64ERR_INPUT_INVALID;
*(uint32_t*)ParamPtr = NETPLAY_CORE_VERSION;
if (ParamInt == NETPLAY_API_VERSION)
return M64ERR_SUCCESS;
else
return M64ERR_INCOMPATIBLE;
case M64CMD_NETPLAY_CLOSE:
return netplay_stop();
default:
return M64ERR_INPUT_INVALID;
}

View file

@ -273,6 +273,28 @@ typedef m64p_error (*ptr_ConfigExternalGetParameter)(m64p_handle, const char *,
EXPORT m64p_error CALL ConfigExternalGetParameter(m64p_handle, const char *, const char *, char *, int);
#endif
/* ConfigSendNetplayConfig()
*
* This function allows plugins to take advantage of the netplay TCP connection
* to send configuration data to the netplay server.
*/
typedef m64p_error (*ptr_ConfigSendNetplayConfig)(char*, int);
#if defined(M64P_CORE_PROTOTYPES)
EXPORT m64p_error CALL ConfigSendNetplayConfig(char*, int);
#endif
/* ConfigReceiveNetplayConfig()
*
* This function allows plugins to take advantage of the netplay TCP connection
* to receive configuration data from the netplay server.
*/
typedef m64p_error (*ptr_ConfigReceiveNetplayConfig)(char*, int);
#if defined(M64P_CORE_PROTOTYPES)
EXPORT m64p_error CALL ConfigReceiveNetplayConfig(char*, int);
#endif
#ifdef __cplusplus
}
#endif

View file

@ -162,7 +162,11 @@ typedef enum {
M64CMD_READ_SCREEN,
M64CMD_RESET,
M64CMD_ADVANCE_FRAME,
M64CMD_SET_MEDIA_LOADER
M64CMD_SET_MEDIA_LOADER,
M64CMD_NETPLAY_INIT,
M64CMD_NETPLAY_CONTROL_PLAYER,
M64CMD_NETPLAY_GET_VERSION,
M64CMD_NETPLAY_CLOSE
} m64p_command;
typedef struct {

View file

@ -28,6 +28,7 @@
#include "backends/api/storage_backend.h"
#include "device/dd/dd_controller.h"
#include "main/util.h"
#include "main/netplay.h"
int open_file_storage(struct file_storage* fstorage, size_t size, const char* filename)
{
@ -42,7 +43,14 @@ int open_file_storage(struct file_storage* fstorage, size_t size, const char* fi
}
/* try to load storage file content */
return read_from_file(fstorage->filename, fstorage->data, fstorage->size);
if (!netplay_is_init())
{
return read_from_file(fstorage->filename, fstorage->data, fstorage->size);
}
else
{
return netplay_read_storage(fstorage->filename, fstorage->data, fstorage->size);
}
}
int open_rom_file_storage(struct file_storage* fstorage, const char* filename)
@ -82,6 +90,9 @@ static size_t file_storage_size(const void* storage)
static void file_storage_save(void* storage)
{
if (netplay_is_init() && netplay_get_controller(0) == -1)
return;
struct file_storage* fstorage = (struct file_storage*)storage;
switch(write_to_file(fstorage->filename, fstorage->data, fstorage->size))

View file

@ -27,6 +27,7 @@
#include "plugin/plugin.h"
#include "main/main.h"
#include "main/netplay.h"
#include <stdint.h>
#include <string.h>
@ -60,8 +61,28 @@ static m64p_error input_plugin_get_input(void* opaque, uint32_t* input_)
int pak_change_requested = 0;
/* first poll controller */
if (input.getKeys) {
input.getKeys(cin_compat->control_id, &keys);
if (!netplay_is_init())
{
if (input.getKeys)
input.getKeys(cin_compat->control_id, &keys);
}
else
{
int netplay_controller = netplay_get_controller(cin_compat->control_id);
if (netplay_controller >= 0)
{
//Here we "trick" the input plugin
//by passing it the controller number that is controlling the player during netplay
uint8_t plugin = Controls[netplay_controller].Plugin;
uint8_t present = Controls[netplay_controller].Present;
if (input.getKeys)
input.getKeys(netplay_controller, &keys);
netplay_set_plugin(cin_compat->control_id, Controls[netplay_controller].Plugin);
Controls[netplay_controller].Plugin = plugin;
Controls[netplay_controller].Present = present;
cin_compat->last_input = keys.Value; //disable pak switching for netplay
}
}
/* return an error if controller is not plugged */
@ -130,6 +151,11 @@ static void input_plugin_rumble_exec(void* opaque, enum rumble_action action)
return;
}
//This is for netplay, -1 means there is no local controller controlling this player
if (control_id == -1) {
return;
}
static const uint8_t rumble_cmd_header[] =
{
0x23, 0x01, /* T=0x23, R=0x01 */
@ -168,6 +194,11 @@ static void input_plugin_read_controller(void* opaque,
return;
}
//This is for netplay, -1 means there is no local controller controlling this player
if (control_id == -1) {
return;
}
/* UGLY: use negative offsets to get access to non-const tx pointer */
input.readController(control_id, rx - 1);
}
@ -182,6 +213,11 @@ void input_plugin_controller_command(void* opaque,
return;
}
//This is for netplay, -1 means there is no local controller controlling this player
if (control_id == -1) {
return;
}
input.controllerCommand(control_id, tx);
}

View file

@ -50,6 +50,9 @@ struct controller_input_compat
unsigned int gb_switch_delay;
unsigned int gb_cart_switch_enabled;
uint32_t netplay_count;
struct netplay_event* event_first;
};
extern const struct controller_input_backend_interface

View file

@ -33,6 +33,7 @@
#include "device/memory/memory.h"
#include "device/r4300/r4300_core.h"
#include "plugin/plugin.h"
#include "main/netplay.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
@ -371,6 +372,8 @@ void update_pif_ram(struct pif* pif)
input.readController(-1, NULL);
}
netplay_update_input(pif);
#ifdef DEBUG_PIF
DebugMessage(M64MSG_INFO, "PIF post read");
print_pif(pif);

View file

@ -64,7 +64,6 @@
#include "device/pif/bootrom_hle.h"
#include "eventloop.h"
#include "main.h"
#include "cheat.h"
#include "osal/files.h"
#include "osal/preproc.h"
#include "osd/osd.h"
@ -76,6 +75,7 @@
#include "savestates.h"
#include "screenshot.h"
#include "util.h"
#include "netplay.h"
#ifdef DBG
#include "debugger/dbg_debugger.h"
@ -327,6 +327,9 @@ int main_set_core_defaults(void)
void main_speeddown(int percent)
{
if (netplay_is_init())
return;
if (l_SpeedFactor - percent > 10) /* 10% minimum speed */
{
l_SpeedFactor -= percent;
@ -338,6 +341,9 @@ void main_speeddown(int percent)
void main_speedup(int percent)
{
if (netplay_is_init())
return;
if (l_SpeedFactor + percent < 300) /* 300% maximum speed */
{
l_SpeedFactor += percent;
@ -349,6 +355,9 @@ void main_speedup(int percent)
static void main_speedset(int percent)
{
if (netplay_is_init())
return;
if (percent < 1 || percent > 1000)
{
DebugMessage(M64MSG_WARNING, "Invalid speed setting %i percent", percent);
@ -365,6 +374,9 @@ static void main_speedset(int percent)
void main_set_fastforward(int enable)
{
if (netplay_is_init())
return;
static int ff_state = 0;
static int SavedSpeedFactor = 100;
@ -395,6 +407,9 @@ void main_set_fastforward(int enable)
static void main_set_speedlimiter(int enable)
{
if (netplay_is_init() && !netplay_lag())
return;
l_MainSpeedLimit = enable ? 1 : 0;
}
@ -408,6 +423,9 @@ void main_toggle_pause(void)
if (!g_EmulatorRunning)
return;
if (netplay_is_init())
return;
if (g_rom_pause)
{
DebugMessage(M64MSG_STATUS, "Emulation continued.");
@ -491,6 +509,9 @@ void main_state_inc_slot(void)
void main_state_load(const char *filename)
{
if (netplay_is_init())
return;
if (filename == NULL) // Save to slot
savestates_set_job(savestates_job_load, savestates_type_m64p, NULL);
else
@ -499,6 +520,9 @@ void main_state_load(const char *filename)
void main_state_save(int format, const char *filename)
{
if (netplay_is_init())
return;
if (filename == NULL) // Save to slot
savestates_set_job(savestates_job_save, savestates_type_m64p, NULL);
else // Save to file
@ -864,6 +888,9 @@ static void apply_speed_limiter(void)
/* TODO: make a GameShark module and move that there */
static void gs_apply_cheats(struct cheat_ctx* ctx)
{
if (netplay_is_init())
return;
struct r4300_core* r4300 = &g_dev.r4300;
if (g_gs_vi_counter < 60)
@ -906,6 +933,8 @@ void new_vi(void)
main_check_inputs();
pause_loop();
netplay_check_sync(&g_dev.r4300.cp0);
}
static void main_switch_pak(int control_id)
@ -1272,12 +1301,12 @@ m64p_error main_run(void)
{
size_t i, k;
size_t rdram_size;
unsigned int count_per_op;
unsigned int emumode;
unsigned int disable_extra_mem;
int si_dma_duration;
int no_compiled_jump;
int randomize_interrupt;
uint32_t count_per_op;
uint32_t emumode;
uint32_t disable_extra_mem;
int32_t si_dma_duration;
int32_t no_compiled_jump;
int32_t randomize_interrupt;
struct file_storage eep;
struct file_storage fla;
struct file_storage sra;
@ -1304,7 +1333,8 @@ m64p_error main_run(void)
savestates_set_autoinc_slot(ConfigGetParamBool(g_CoreConfig, "AutoStateSlotIncrement"));
savestates_select_slot(ConfigGetParamInt(g_CoreConfig, "CurrentStateSlot"));
no_compiled_jump = ConfigGetParamBool(g_CoreConfig, "NoCompiledJump");
randomize_interrupt = ConfigGetParamBool(g_CoreConfig, "RandomizeInterrupt");
//We disable any randomness for netplay
randomize_interrupt = !netplay_is_init() ? ConfigGetParamBool(g_CoreConfig, "RandomizeInterrupt") : 0;
count_per_op = ConfigGetParamInt(g_CoreConfig, "CountPerOp");
if (ROM_PARAMS.disableextramem)
@ -1322,6 +1352,9 @@ m64p_error main_run(void)
if (si_dma_duration < 0)
si_dma_duration = ROM_PARAMS.sidmaduration;
//During netplay, player 1 is the source of truth for these settings
netplay_sync_settings(&count_per_op, &disable_extra_mem, &si_dma_duration, &emumode, &no_compiled_jump);
cheat_add_hacks(&g_cheat_ctx, ROM_PARAMS.cheats);
/* do byte-swapping if it hasn't been done yet */
@ -1400,9 +1433,13 @@ m64p_error main_run(void)
memset(&l_gb_carts_data, 0, GAME_CONTROLLERS_COUNT*sizeof(*l_gb_carts_data));
memset(cin_compats, 0, GAME_CONTROLLERS_COUNT*sizeof(*cin_compats));
netplay_read_registration(cin_compats);
for (i = 0; i < GAME_CONTROLLERS_COUNT; ++i) {
control_ids[i] = (int)i;
//During netplay, we "trick" the input plugin
//by replacing the regular control_id with the ID that is controlling the player during netplay
control_ids[i] = netplay_is_init() ? netplay_get_controller(i) : (int)i;
/* if input plugin requests RawData let the input plugin do the channel device processing */
if (Controls[i].RawData) {
@ -1425,6 +1462,9 @@ m64p_error main_run(void)
cin_compats[i].cont = &g_dev.controllers[i];
cin_compats[i].tpk = &g_dev.transferpaks[i];
cin_compats[i].last_pak_type = Controls[i].Plugin;
cin_compats[i].last_input = 0;
cin_compats[i].netplay_count = 0;
cin_compats[i].event_first = NULL;
l_gb_carts_data[i].control_id = (int)i;

669
src/main/netplay.c Normal file
View file

@ -0,0 +1,669 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus - netplay.c *
* Mupen64Plus homepage: https://mupen64plus.org/ *
* Copyright (C) 2020 loganmc10 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#define M64P_CORE_PROTOTYPES 1
#include "api/callbacks.h"
#include "main.h"
#include "util.h"
#include "plugin/plugin.h"
#include "backends/plugins_compat/plugins_compat.h"
#include "netplay.h"
#include <SDL_net.h>
static int l_canFF;
static int l_netplay_controller;
static int l_netplay_control[4];
static UDPsocket l_udpSocket;
static TCPsocket l_tcpSocket;
static int l_udpChannel;
static int l_spectator;
static int l_netplay_is_init = 0;
static uint32_t l_vi_counter;
static uint8_t l_status;
static uint32_t l_reg_id;
static struct controller_input_compat *l_cin_compats;
static uint8_t l_plugin[4];
static uint8_t l_buffer_target;
static uint8_t l_player_lag[4];
//UDP packet formats
#define UDP_SEND_KEY_INFO 0
#define UDP_RECEIVE_KEY_INFO 1
#define UDP_REQUEST_KEY_INFO 2
#define UDP_SYNC_DATA 4
//TCP packet formats
#define TCP_SEND_SAVE 1
#define TCP_RECEIVE_SAVE 2
#define TCP_SEND_SETTINGS 3
#define TCP_RECEIVE_SETTINGS 4
#define TCP_REGISTER_PLAYER 5
#define TCP_GET_REGISTRATION 6
#define TCP_DISCONNECT_NOTICE 7
m64p_error netplay_start(const char* host, int port)
{
if (SDLNet_Init() < 0)
{
DebugMessage(M64MSG_ERROR, "Netplay: Could not initialize SDL Net library");
return M64ERR_SYSTEM_FAIL;
}
l_udpSocket = SDLNet_UDP_Open(0);
if (l_udpSocket == NULL)
{
DebugMessage(M64MSG_ERROR, "Netplay: UDP socket creation failed");
return M64ERR_SYSTEM_FAIL;
}
IPaddress dest;
SDLNet_ResolveHost(&dest, host, port);
l_udpChannel = SDLNet_UDP_Bind(l_udpSocket, -1, &dest);
if (l_udpChannel < 0)
{
DebugMessage(M64MSG_ERROR, "Netplay: could not bind to UDP socket");
SDLNet_UDP_Close(l_udpSocket);
l_udpSocket = NULL;
return M64ERR_SYSTEM_FAIL;
}
l_tcpSocket = SDLNet_TCP_Open(&dest);
if (l_tcpSocket == NULL)
{
DebugMessage(M64MSG_ERROR, "Netplay: could not bind to TCP socket");
SDLNet_UDP_Close(l_udpSocket);
l_udpSocket = NULL;
return M64ERR_SYSTEM_FAIL;
}
for (int i = 0; i < 4; ++i)
{
l_netplay_control[i] = -1;
l_plugin[i] = 0;
l_player_lag[i] = 0;
}
l_canFF = 0;
l_netplay_controller = 0;
l_netplay_is_init = 1;
l_spectator = 1;
l_vi_counter = 0;
l_status = 0;
l_reg_id = 0;
return M64ERR_SUCCESS;
}
m64p_error netplay_stop()
{
if (l_udpSocket == NULL)
return M64ERR_INVALID_STATE;
else
{
char output_data[5];
output_data[0] = TCP_DISCONNECT_NOTICE;
SDLNet_Write32(l_reg_id, &output_data[1]);
SDLNet_TCP_Send(l_tcpSocket, &output_data[0], 5);
SDLNet_UDP_Unbind(l_udpSocket, l_udpChannel);
SDLNet_UDP_Close(l_udpSocket);
SDLNet_TCP_Close(l_tcpSocket);
l_tcpSocket = NULL;
l_udpSocket = NULL;
l_udpChannel = -1;
l_netplay_is_init = 0;
SDLNet_Quit();
return M64ERR_SUCCESS;
}
}
int netplay_is_init()
{
return l_netplay_is_init;
}
static uint8_t buffer_size(uint8_t control_id)
{
//This function returns the size of the local input buffer
uint8_t counter = 0;
struct netplay_event* current = l_cin_compats[control_id].event_first;
while (current != NULL)
{
current = current->next;
++counter;
}
return counter;
}
static void netplay_request_input(uint8_t control_id)
{
UDPpacket *packet = SDLNet_AllocPacket(12);
packet->data[0] = UDP_REQUEST_KEY_INFO;
packet->data[1] = control_id; //The player we need input for
SDLNet_Write32(l_reg_id, &packet->data[2]); //our registration ID
SDLNet_Write32(l_cin_compats[control_id].netplay_count, &packet->data[6]); //the current event count
packet->data[10] = l_spectator; //whether we are a spectator
packet->data[11] = buffer_size(control_id); //our local buffer size
packet->len = 12;
SDLNet_UDP_Send(l_udpSocket, l_udpChannel, packet);
SDLNet_FreePacket(packet);
}
static int check_valid(uint8_t control_id, uint32_t count)
{
//Check if we already have this event recorded locally, returns 1 if we do
struct netplay_event* current = l_cin_compats[control_id].event_first;
while (current != NULL)
{
if (current->count == count) //event already recorded
return 1;
current = current->next;
}
return 0;
}
static int netplay_require_response(void* opaque)
{
//This function runs inside a thread.
//It runs if our local buffer size is 0 (we need to execute a key event, but we don't have the data we need).
//We basically beg the server for input data.
//After 10 seconds a timeout occurs, we assume we have lost connection to the server.
uint8_t control_id = *(uint8_t*)opaque;
uint32_t timeout = SDL_GetTicks() + 10000;
while (!check_valid(control_id, l_cin_compats[control_id].netplay_count))
{
if (SDL_GetTicks() > timeout)
{
l_udpChannel = -1;
return 0;
}
netplay_request_input(control_id);
SDL_Delay(5);
}
return 1;
}
static void netplay_process()
{
//In this function we process data we have received from the server
UDPpacket *packet = SDLNet_AllocPacket(512);
uint32_t curr, count, keys;
uint8_t plugin, player, current_status;
while (SDLNet_UDP_Recv(l_udpSocket, packet) == 1)
{
switch (packet->data[0])
{
case UDP_RECEIVE_KEY_INFO:
player = packet->data[1];
//current_status is a status update from the server
//it will let us know if another player has disconnected, or the games have desynced
current_status = packet->data[2];
l_player_lag[player] = packet->data[3];
if (current_status != l_status)
{
if (((current_status & 0x1) ^ (l_status & 0x1)) != 0)
DebugMessage(M64MSG_ERROR, "Netplay: players have de-synced at VI %u", l_vi_counter);
for (int dis = 1; dis < 5; ++dis)
{
if (((current_status & (0x1 << dis)) ^ (l_status & (0x1 << dis))) != 0)
DebugMessage(M64MSG_ERROR, "Netplay: player %u has disconnected", dis);
}
l_status = current_status;
}
curr = 5;
//this loop processes input data from the server, inserting new events into the linked list for each player
//it skips events that we have already recorded, or if we receive data for an event that has already happened
for (uint8_t i = 0; i < packet->data[4]; ++i)
{
count = SDLNet_Read32(&packet->data[curr]);
curr += 4;
if (((count - l_cin_compats[player].netplay_count) > (UINT32_MAX / 2)) || (check_valid(player, count))) //event doesn't need to be recorded
{
curr += 5;
continue;
}
keys = SDLNet_Read32(&packet->data[curr]);
curr += 4;
plugin = packet->data[curr];
curr += 1;
//insert new event at beginning of linked list
struct netplay_event* new_event = (struct netplay_event*)malloc(sizeof(struct netplay_event));
new_event->count = count;
new_event->buttons = keys;
new_event->plugin = plugin;
new_event->next = l_cin_compats[player].event_first;
l_cin_compats[player].event_first = new_event;
}
break;
default:
DebugMessage(M64MSG_ERROR, "Netplay: received unknown message from server");
break;
}
}
SDLNet_FreePacket(packet);
}
static int netplay_ensure_valid(uint8_t control_id)
{
//This function makes sure we have data for a certain event
//If we don't have the data, it will create a new thread that will request the data
if (check_valid(control_id, l_cin_compats[control_id].netplay_count))
return 1;
if (l_udpChannel == -1)
return 0;
#if SDL_VERSION_ATLEAST(2,0,0)
SDL_Thread* thread = SDL_CreateThread(netplay_require_response, "Netplay key request", &control_id);
#else
SDL_Thread* thread = SDL_CreateThread(netplay_require_response, &control_id);
#endif
while (!check_valid(control_id, l_cin_compats[control_id].netplay_count) && l_udpChannel != -1)
netplay_process();
int success;
SDL_WaitThread(thread, &success);
return success;
}
static void netplay_delete_event(struct netplay_event* current, uint8_t control_id)
{
//This function deletes an event from the linked list
struct netplay_event* find = l_cin_compats[control_id].event_first;
while (find != NULL)
{
if (find->next == current)
{
find->next = current->next;
break;
}
find = find->next;
}
if (current == l_cin_compats[control_id].event_first)
l_cin_compats[control_id].event_first = l_cin_compats[control_id].event_first->next;
free(current);
}
static uint32_t netplay_get_input(uint8_t control_id)
{
uint32_t keys;
netplay_process();
netplay_request_input(control_id);
//l_buffer_target is set by the server upon registration
//l_player_lag is how far behind we are from the lead player
//buffer_size is the local buffer size
if (l_player_lag[control_id] > 0 && buffer_size(control_id) > l_buffer_target)
{
l_canFF = 1;
main_core_state_set(M64CORE_SPEED_LIMITER, 0);
}
else
{
main_core_state_set(M64CORE_SPEED_LIMITER, 1);
l_canFF = 0;
}
if (netplay_ensure_valid(control_id))
{
//We grab the event from the linked list, the delete it once it has been used
//Finally we increment the event counter
struct netplay_event* current = l_cin_compats[control_id].event_first;
while (current->count != l_cin_compats[control_id].netplay_count)
current = current->next;
keys = current->buttons;
Controls[control_id].Plugin = current->plugin;
netplay_delete_event(current, control_id);
++l_cin_compats[control_id].netplay_count;
}
else
{
DebugMessage(M64MSG_ERROR, "Netplay: lost connection to server");
main_core_state_set(M64CORE_EMU_STATE, M64EMU_STOPPED);
keys = 0;
}
return keys;
}
static void netplay_send_input(uint8_t control_id, uint32_t keys)
{
UDPpacket *packet = SDLNet_AllocPacket(11);
packet->data[0] = UDP_SEND_KEY_INFO;
packet->data[1] = control_id; //player number
SDLNet_Write32(l_cin_compats[control_id].netplay_count, &packet->data[2]); // current event count
SDLNet_Write32(keys, &packet->data[6]); //key data
packet->data[10] = l_plugin[control_id]; //current plugin
packet->len = 11;
SDLNet_UDP_Send(l_udpSocket, l_udpChannel, packet);
SDLNet_FreePacket(packet);
}
uint8_t netplay_register_player(uint8_t player, uint8_t plugin, uint8_t rawdata, uint32_t reg_id)
{
l_reg_id = reg_id;
char output_data[8];
output_data[0] = TCP_REGISTER_PLAYER;
output_data[1] = player; //player number we'd like to register
output_data[2] = plugin; //current plugin
output_data[3] = rawdata; //whether we are using a RawData input plugin
SDLNet_Write32(l_reg_id, &output_data[4]);
SDLNet_TCP_Send(l_tcpSocket, &output_data[0], 8);
uint8_t response[2];
size_t recv = 0;
while (recv < 2)
recv += SDLNet_TCP_Recv(l_tcpSocket, &response[recv], 2 - recv);
l_buffer_target = response[1]; //local buffer size target
return response[0];
}
int netplay_lag()
{
return l_canFF;
}
int netplay_next_controller()
{
return l_netplay_controller;
}
void netplay_set_controller(uint8_t player)
{
l_netplay_control[player] = l_netplay_controller++;
l_spectator = 0;
}
int netplay_get_controller(uint8_t player)
{
return l_netplay_control[player];
}
file_status_t netplay_read_storage(const char *filename, void *data, size_t size)
{
//This function syncs save games.
//If the client is controlling player 1, it sends its save game to the server
//All other players receive save files from the server
const char *short_filename = strrchr(filename, '/');
if (short_filename == NULL)
short_filename = strrchr(filename, '\\');
short_filename += 1;
uint32_t buffer_pos = 0;
char *output_data = malloc(size + strlen(short_filename) + 6);
file_status_t ret;
uint8_t request;
if (l_netplay_control[0] != -1)
{
request = TCP_SEND_SAVE;
memcpy(&output_data[buffer_pos], &request, 1);
++buffer_pos;
//send file name
memcpy(&output_data[buffer_pos], short_filename, strlen(short_filename) + 1);
buffer_pos += strlen(short_filename) + 1;
ret = read_from_file(filename, data, size);
if (ret == file_open_error)
memset(data, 0, size); //all zeros means there is no save file
SDLNet_Write32((int32_t)size, &output_data[buffer_pos]); //file data size
buffer_pos += 4;
memcpy(&output_data[buffer_pos], data, size); //file data
buffer_pos += size;
SDLNet_TCP_Send(l_tcpSocket, &output_data[0], buffer_pos);
}
else
{
request = TCP_RECEIVE_SAVE;
memcpy(&output_data[buffer_pos], &request, 1);
++buffer_pos;
//name of the file we are requesting
memcpy(&output_data[buffer_pos], short_filename, strlen(short_filename) + 1);
buffer_pos += strlen(short_filename) + 1;
SDLNet_TCP_Send(l_tcpSocket, &output_data[0], buffer_pos);
size_t recv = 0;
char *data_array = data;
while (recv < size)
recv += SDLNet_TCP_Recv(l_tcpSocket, data_array + recv, size - recv);
int sum = 0;
for (int i = 0; i < size; ++i)
sum |= data_array[i];
if (sum == 0) //all zeros means there is no save file
ret = file_open_error;
else
ret = file_ok;
}
free(output_data);
return ret;
}
void netplay_sync_settings(uint32_t *count_per_op, uint32_t *disable_extra_mem, int32_t *si_dma_duration, uint32_t *emumode, int32_t *no_compiled_jump)
{
if (!netplay_is_init())
return;
char output_data[21];
uint8_t request;
if (l_netplay_control[0] != -1) //player 1 is the source of truth for settings
{
request = TCP_SEND_SETTINGS;
memcpy(&output_data[0], &request, 1);
SDLNet_Write32(*count_per_op, &output_data[1]);
SDLNet_Write32(*disable_extra_mem, &output_data[5]);
SDLNet_Write32(*si_dma_duration, &output_data[9]);
SDLNet_Write32(*emumode, &output_data[13]);
SDLNet_Write32(*no_compiled_jump, &output_data[17]);
SDLNet_TCP_Send(l_tcpSocket, &output_data[0], 21);
}
else
{
request = TCP_RECEIVE_SETTINGS;
memcpy(&output_data[0], &request, 1);
SDLNet_TCP_Send(l_tcpSocket, &output_data[0], 1);
int32_t recv = 0;
while (recv < 20)
recv += SDLNet_TCP_Recv(l_tcpSocket, &output_data[recv], 20 - recv);
*count_per_op = SDLNet_Read32(&output_data[0]);
*disable_extra_mem = SDLNet_Read32(&output_data[4]);
*si_dma_duration = SDLNet_Read32(&output_data[8]);
*emumode = SDLNet_Read32(&output_data[12]);
*no_compiled_jump = SDLNet_Read32(&output_data[16]);
}
}
void netplay_check_sync(struct cp0* cp0)
{
//This function is used to check if games have desynced
//Every 60 VIs, it sends the value of the CP0 registers to the server
//The server will compare the values, and update the status byte if it detects a desync
if (!netplay_is_init())
return;
const uint32_t* cp0_regs = r4300_cp0_regs(cp0);
if (l_vi_counter % 60 == 0)
{
uint32_t packet_len = (CP0_REGS_COUNT * 4) + 5;
UDPpacket *packet = SDLNet_AllocPacket(packet_len);
packet->data[0] = UDP_SYNC_DATA;
SDLNet_Write32(l_vi_counter, &packet->data[1]); //current VI count
for (int i = 0; i < CP0_REGS_COUNT; ++i)
{
SDLNet_Write32(cp0_regs[i], &packet->data[(i * 4) + 5]);
}
packet->len = packet_len;
SDLNet_UDP_Send(l_udpSocket, l_udpChannel, packet);
SDLNet_FreePacket(packet);
}
++l_vi_counter;
}
void netplay_read_registration(struct controller_input_compat* cin_compats)
{
//This function runs right before the game starts
//The server shares the registration details about each player
if (!netplay_is_init())
return;
l_cin_compats = cin_compats;
uint32_t reg_id;
char output_data = TCP_GET_REGISTRATION;
char input_data[24];
SDLNet_TCP_Send(l_tcpSocket, &output_data, 1);
size_t recv = 0;
while (recv < 24)
recv += SDLNet_TCP_Recv(l_tcpSocket, &input_data[recv], 24 - recv);
uint32_t curr = 0;
for (int i = 0; i < 4; ++i)
{
reg_id = SDLNet_Read32(&input_data[curr]);
curr += 4;
if (reg_id == 0) //No one registered to control this player
{
Controls[i].Present = 0;
Controls[i].Plugin = 1;
Controls[i].RawData = 0;
curr += 2;
}
else
{
Controls[i].Present = 1;
Controls[i].Plugin = input_data[curr];
l_plugin[i] = Controls[i].Plugin;
++curr;
Controls[i].RawData = input_data[curr];
++curr;
}
}
}
static void netplay_send_raw_input(struct pif* pif)
{
for (int i = 0; i < 4; ++i)
{
if (l_netplay_control[i] != -1)
{
if (pif->channels[i].tx && pif->channels[i].tx_buf[0] == JCMD_CONTROLLER_READ)
netplay_send_input(i, *(uint32_t*)pif->channels[i].rx_buf);
}
}
}
static void netplay_get_raw_input(struct pif* pif)
{
for (int i = 0; i < 4; ++i)
{
if (Controls[i].Present == 1)
{
if (pif->channels[i].tx)
{
*pif->channels[i].rx &= ~0xC0; //Always show the controller as connected
if(pif->channels[i].tx_buf[0] == JCMD_CONTROLLER_READ)
{
*(uint32_t*)pif->channels[i].rx_buf = netplay_get_input(i);
}
else if ((pif->channels[i].tx_buf[0] == JCMD_STATUS || pif->channels[i].tx_buf[0] == JCMD_RESET) && Controls[i].RawData)
{
//a bit of a hack for raw input controllers, force the status
uint16_t type = JDT_JOY_ABS_COUNTERS | JDT_JOY_PORT;
pif->channels[i].rx_buf[0] = (uint8_t)(type >> 0);
pif->channels[i].rx_buf[1] = (uint8_t)(type >> 8);
pif->channels[i].rx_buf[2] = 0;
}
else if (pif->channels[i].tx_buf[0] == JCMD_PAK_READ && Controls[i].RawData)
{
//also a hack for raw input, we return "mempak not present" if the game tries to read the mempak
pif->channels[i].rx_buf[32] = 255;
}
else if (pif->channels[i].tx_buf[0] == JCMD_PAK_WRITE && Controls[i].RawData)
{
//also a hack for raw input, we return "mempak not present" if the game tries to write to mempak
pif->channels[i].rx_buf[0] = 255;
}
}
}
}
}
void netplay_update_input(struct pif* pif)
{
if (netplay_is_init())
{
netplay_send_raw_input(pif);
netplay_get_raw_input(pif);
}
}
void netplay_set_plugin(uint8_t control_id, uint8_t plugin)
{
if (!(control_id > 0 && plugin == 2)) //Only P1 can use mempak
l_plugin[control_id] = plugin;
}
m64p_error netplay_send_config(char* data, int size)
{
if (!netplay_is_init())
return M64ERR_NOT_INIT;
if (l_netplay_control[0] != -1 || size == 1) //Only P1 sends settings, we allow all players to send if the size is 1, this may be a request packet
{
int result = SDLNet_TCP_Send(l_tcpSocket, data, size);
if (result < size)
return M64ERR_SYSTEM_FAIL;
return M64ERR_SUCCESS;
}
else
return M64ERR_INVALID_STATE;
}
m64p_error netplay_receive_config(char* data, int size)
{
if (!netplay_is_init())
return M64ERR_NOT_INIT;
if (l_netplay_control[0] == -1) //Only P2-4 receive settings
{
int recv = 0;
while (recv < size)
{
recv += SDLNet_TCP_Recv(l_tcpSocket, &data[recv], size - recv);
if (recv < 1)
return M64ERR_SYSTEM_FAIL;
}
return M64ERR_SUCCESS;
}
else
return M64ERR_INVALID_STATE;
}

137
src/main/netplay.h Normal file
View file

@ -0,0 +1,137 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus - netplay.h *
* Mupen64Plus homepage: https://mupen64plus.org/ *
* Copyright (C) 2020 loganmc10 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef __NETPLAY_H__
#define __NETPLAY_H__
#include "device/r4300/cp0.h"
#include "device/pif/pif.h"
#include "main/util.h"
#define NETPLAY_CORE_VERSION 1
struct netplay_event {
uint32_t buttons;
uint8_t plugin;
uint32_t count;
struct netplay_event* next;
};
struct controller_input_compat;
#ifdef M64P_NETPLAY
m64p_error netplay_start(const char* host, int port);
m64p_error netplay_stop();
uint8_t netplay_register_player(uint8_t player, uint8_t plugin, uint8_t rawdata, uint32_t reg_id);
int netplay_lag();
void netplay_set_controller(uint8_t player);
int netplay_is_init();
int netplay_get_controller(uint8_t player);
file_status_t netplay_read_storage(const char *filename, void *data, size_t size);
void netplay_sync_settings(uint32_t *count_per_op, uint32_t *disable_extra_mem, int32_t *si_dma_duration, uint32_t *emumode, int32_t *no_compiled_jump);
void netplay_check_sync(struct cp0* cp0);
int netplay_next_controller();
void netplay_read_registration(struct controller_input_compat* cin_compats);
void netplay_update_input(struct pif* pif);
void netplay_set_plugin(uint8_t control_id, uint8_t plugin);
m64p_error netplay_send_config(char* data, int size);
m64p_error netplay_receive_config(char* data, int size);
#else
static osal_inline m64p_error netplay_start(const char* host, int port)
{
return M64ERR_INCOMPATIBLE;
}
static osal_inline m64p_error netplay_stop()
{
return M64ERR_INCOMPATIBLE;
}
static osal_inline uint8_t netplay_register_player(uint8_t player, uint8_t plugin, uint8_t rawdata, uint32_t reg_id)
{
return 0;
}
static osal_inline int netplay_lag()
{
return 0;
}
static osal_inline void netplay_set_controller(uint8_t player)
{
}
static osal_inline int netplay_is_init()
{
return 0;
}
static osal_inline int netplay_get_controller(uint8_t player)
{
return 0;
}
static osal_inline file_status_t netplay_read_storage(const char *filename, void *data, size_t size)
{
return 0;
}
static osal_inline void netplay_sync_settings(uint32_t *count_per_op, uint32_t *disable_extra_mem, int32_t *si_dma_duration, uint32_t *emumode, int32_t *no_compiled_jump)
{
}
static osal_inline void netplay_check_sync(struct cp0* cp0)
{
}
static osal_inline int netplay_next_controller()
{
return 0;
}
static osal_inline void netplay_read_registration(struct controller_input_compat* cin_compats)
{
}
static osal_inline void netplay_update_input(struct pif* pif)
{
}
static osal_inline void netplay_set_plugin(uint8_t control_id, uint8_t plugin)
{
}
static osal_inline m64p_error netplay_send_config(char* data, int size)
{
return M64ERR_INCOMPATIBLE;
}
static osal_inline m64p_error netplay_receive_config(char* data, int size)
{
return M64ERR_INCOMPATIBLE;
}
#endif
#endif

View file

@ -31,6 +31,7 @@
#define CONFIG_API_VERSION 0x020301
#define DEBUG_API_VERSION 0x020001
#define VIDEXT_API_VERSION 0x030100
#define NETPLAY_API_VERSION 0x010000
#define VERSION_PRINTF_SPLIT(x) (((x) >> 16) & 0xffff), (((x) >> 8) & 0xff), ((x) & 0xff)