From 34b1ec76f65e6da418cee86cf22900c114fdbdfd Mon Sep 17 00:00:00 2001 From: MysterD Date: Sat, 1 Aug 2020 20:22:24 -0700 Subject: [PATCH] Started adding networking --- Makefile | 4 +- build-windows-visual-studio/sm64ex.vcxproj | 5 + .../sm64ex.vcxproj.filters | 17 +++ cgdb.exe.stackdump | 14 ++ levels/castle_grounds/script.c | 4 +- network.sh | 5 + src/game/mario.c | 17 ++- src/pc/cliopts.c | 6 + src/pc/cliopts.h | 7 + src/pc/network/network.c | 140 ++++++++++++++++++ src/pc/network/network.h | 13 ++ src/pc/pc_main.c | 5 + 12 files changed, 227 insertions(+), 10 deletions(-) create mode 100644 cgdb.exe.stackdump create mode 100644 network.sh create mode 100644 src/pc/network/network.c create mode 100644 src/pc/network/network.h diff --git a/Makefile b/Makefile index ae83b953..26e3667d 100644 --- a/Makefile +++ b/Makefile @@ -286,7 +286,7 @@ LEVEL_DIRS := $(patsubst levels/%,%,$(dir $(wildcard levels/*/header.h))) # Directories containing source files # Hi, I'm a PC -SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes +SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes src/pc/network ASM_DIRS := ifeq ($(DISCORDRPC),1) @@ -617,7 +617,7 @@ ifeq ($(TARGET_WEB),1) LDFLAGS := -lm -lGL -lSDL2 -no-pie -s TOTAL_MEMORY=20MB -g4 --source-map-base http://localhost:8080/ -s "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain']" else ifeq ($(WINDOWS_BUILD),1) - LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -Llib -lpthread $(BACKEND_LDFLAGS) -static + LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -Llib -L"ws2_32" -lwsock32 -lpthread $(BACKEND_LDFLAGS) -static ifeq ($(CROSS),) LDFLAGS += -no-pie endif diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj index 16c9f1ca..389ef6e0 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj +++ b/build-windows-visual-studio/sm64ex.vcxproj @@ -3943,6 +3943,7 @@ + @@ -4280,6 +4281,10 @@ + + + + diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters index fe074953..694908d9 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj.filters +++ b/build-windows-visual-studio/sm64ex.vcxproj.filters @@ -3412,6 +3412,12 @@ {c3e4f1bd-5aac-4a18-90cf-7a3e74132018} + + {4c8b8213-2929-496d-8b5b-2a06d5880d2b} + + + {1c96986a-a883-42fa-adb0-1f0998af2961} + @@ -14931,6 +14937,9 @@ Source Files\actors\mario + + Source Files\src\pc\network + @@ -15831,4 +15840,12 @@ Header Files\tools + + + Header Files\src\pc\network + + + Header Files\src\pc\network + + \ No newline at end of file diff --git a/cgdb.exe.stackdump b/cgdb.exe.stackdump new file mode 100644 index 00000000..7d41bad2 --- /dev/null +++ b/cgdb.exe.stackdump @@ -0,0 +1,14 @@ +Stack trace: +Frame Function Args +000FFFFB418 00180063480 (000FFFFB638, 00000000002, 00000000000, 000FFFFDE50) +000FFFFDE50 0018006563C (000FFFFBC70, 00000000000, 0000000032C, 00000000000) +000FFFFBB40 00180147028 (7FFEB01920AA, 00000000000, 7FFEB285B55F, 00000000000) +000FFFFBE80 00180170764 (00000000000, 00000000000, 00000000000, 00000000000) +000FFFFBE80 0018014332B (00180269458, 00180148F2E, 0010042D040, 000000002D8) +000FFFFBE80 00180215058 (0010042D040, 0010042D08F, 000000002D8, 001802693DF) +000FFFFBE80 00180043517 (000FFFFBEF0, 00000000004, 00000000000, 00100000000) +000FFFFC710 00100408373 (00000000000, 00000000000, 001801C7B4B, 00000000000) +000FFFFC710 00180065505 (008000DF9CC, 00180000000, 7FFEB016B80D, 00000000000) +000FFFFC710 0018013E4D0 (008000DF9CC, 00180000000, 7FFEB016B80D, 00000000000) +000FFFFC710 005FCB3C5E4 (008000DF9CC, 00180000000, 7FFEB016B80D, 00000000000) +End of stack trace diff --git a/levels/castle_grounds/script.c b/levels/castle_grounds/script.c index 501bd1b2..63c5ba48 100644 --- a/levels/castle_grounds/script.c +++ b/levels/castle_grounds/script.c @@ -90,8 +90,8 @@ static const LevelScript script_func_local_4[] = { OBJECT(/*model*/ MODEL_BUTTERFLY, /*pos*/ -1204, 326, 3296, /*angle*/ 0, 0, 0, /*behParam*/ 0x00000000, /*beh*/ bhvButterfly), OBJECT(/*model*/ MODEL_YOSHI, /*pos*/ 0, 3174, -5625, /*angle*/ 0, 0, 0, /*behParam*/ 0x00000000, /*beh*/ bhvYoshi), // TESTING BELOW - OBJECT(/*model*/ MODEL_BLACK_BOBOMB, /*pos*/ -2028, 260, 4664, /*angle*/ 0, 0, 0, /*behParam*/ 0x00000000, /*beh*/ bhvBobomb), - OBJECT(/*model*/ MODEL_NONE, /*pos*/ -2028, 260, 3264, /*angle*/ 0, 0, 0, /*behParam*/ 0x00000000, /*beh*/ bhvGoombaTripletSpawner), + //OBJECT(/*model*/ MODEL_BLACK_BOBOMB, /*pos*/ -2028, 260, 4664, /*angle*/ 0, 0, 0, /*behParam*/ 0x00000000, /*beh*/ bhvBobomb), + //OBJECT(/*model*/ MODEL_NONE, /*pos*/ -2028, 260, 3264, /*angle*/ 0, 0, 0, /*behParam*/ 0x00000000, /*beh*/ bhvGoombaTripletSpawner), RETURN(), }; diff --git a/network.sh b/network.sh new file mode 100644 index 00000000..cb5dac17 --- /dev/null +++ b/network.sh @@ -0,0 +1,5 @@ +set -e +make BETTERCAMERA=1 NODRAWINGDISTANCE=1 DEBUG=1 IMMEDIATELOAD=1 +./build/us_pc/sm64.us.f3dex2e.exe --server --configfile sm64config_server.txt & +./build/us_pc/sm64.us.f3dex2e.exe --client --configfile sm64config_client.txt & +#winpty cgdb ./build/us_pc/sm64.us.f3dex2e.exe -ex 'b act_jump_kick' -ex 'run --client --configfile sm64config_client.txt' -ex 'quit' diff --git a/src/game/mario.c b/src/game/mario.c index eef2536e..e4a63227 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -39,6 +39,8 @@ #include "bettercamera.h" #endif +#include "../pc/network/network.h" + u32 unused80339F10; s8 filler80339F1C[20]; @@ -1276,8 +1278,8 @@ void debug_print_speed_action_normal(struct MarioState *m) { * Update the button inputs for Mario. */ void update_mario_button_inputs(struct MarioState *m) { - // disable Luigi inputs - //if (m != &gMarioStates[1]) { return; } + // don't update Luigi inputs + if (m != &gMarioStates[0]) { return; } if (m->controller->buttonPressed & A_BUTTON) { m->input |= INPUT_A_PRESSED; @@ -1319,8 +1321,8 @@ void update_mario_button_inputs(struct MarioState *m) { * Updates the joystick intended magnitude. */ void update_mario_joystick_inputs(struct MarioState *m) { - // disable Luigi inputs - //if (m != &gMarioStates[1]) { return; } + // don't update Luigi inputs + if (m != &gMarioStates[0]) { return; } struct Controller *controller = m->controller; f32 mag = ((controller->stickMag / 64.0f) * (controller->stickMag / 64.0f)) * 64.0f; @@ -1410,7 +1412,8 @@ void update_mario_geometry_inputs(struct MarioState *m) { */ void update_mario_inputs(struct MarioState *m) { m->particleFlags = 0; - m->input = 0; + if (m == &gMarioStates[0]) { m->input = 0; } + m->collidedObjInteractTypes = m->marioObj->collidedObjInteractTypes; m->flags &= 0xFFFFFF; @@ -1937,6 +1940,8 @@ skippy: init_mario(); gMarioState = &gMarioStates[0]; } + + network_track(gMarioStates); } void init_mario_from_save_file(void) { @@ -1948,7 +1953,7 @@ void init_mario_from_save_file(void) { gMarioState->spawnInfo = &gPlayerSpawnInfos[i]; gMarioState->statusForCamera = &gPlayerCameraState[i]; gMarioState->marioBodyState = &gBodyStates[i]; - gMarioState->controller = &gControllers[0]; + gMarioState->controller = &gControllers[i]; gMarioState->animation = &D_80339D10[i]; gMarioState->numCoins = 0; diff --git a/src/pc/cliopts.c b/src/pc/cliopts.c index b9e9ee77..7d9cbe23 100644 --- a/src/pc/cliopts.c +++ b/src/pc/cliopts.c @@ -48,6 +48,12 @@ void parse_cli_opts(int argc, char* argv[]) { else if (strcmp(argv[i], "--windowed") == 0) // Open game in windowed mode gCLIOpts.FullScreen = 2; + else if (strcmp(argv[i], "--server") == 0) // Host server + gCLIOpts.Network = NT_SERVER; + + else if (strcmp(argv[i], "--client") == 0) // Join server + gCLIOpts.Network = NT_CLIENT; + else if (strcmp(argv[i], "--cheats") == 0) // Enable cheats menu Cheats.EnableCheats = true; diff --git a/src/pc/cliopts.h b/src/pc/cliopts.h index f9a45e18..f51264e8 100644 --- a/src/pc/cliopts.h +++ b/src/pc/cliopts.h @@ -3,9 +3,16 @@ #include "platform.h" +enum NetworkType { + NT_NONE, + NT_SERVER, + NT_CLIENT +}; + struct PCCLIOptions { unsigned int SkipIntro; unsigned int FullScreen; + enum NetworkType Network; char ConfigFile[SYS_MAX_PATH]; char SavePath[SYS_MAX_PATH]; char GameDir[SYS_MAX_PATH]; diff --git a/src/pc/network/network.c b/src/pc/network/network.c new file mode 100644 index 00000000..1317ad4d --- /dev/null +++ b/src/pc/network/network.c @@ -0,0 +1,140 @@ +////////////////////////// +// TODO: port to linux! // +////////////////////////// + +#ifndef UNICODE +#define UNICODE +#endif + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include + +// Link with ws2_32.lib +#pragma comment(lib, "Ws2_32.lib") + +#include "network.h" + +#define NETWORKTYPESTR (networkType == NT_CLIENT ? "Client" : "Server") +enum NetworkType networkType; +SOCKET gSocket; +#define BUFFER_LENGTH 1024 +unsigned short txPort; +char* txIpAddr = "127.0.0.1"; + +struct MarioState *marioStates = NULL; + +void network_init(enum NetworkType inNetworkType) { + networkType = inNetworkType; + if (networkType == NT_NONE) { return; } + + //----------------------------------------------- + // Initialize Winsock + WSADATA wsaData; + int rc = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (rc != NO_ERROR) { + wprintf(L"%s WSAStartup failed with error %d\n", NETWORKTYPESTR, rc); + return; + } + //----------------------------------------------- + // Create a receiver socket to receive datagrams + gSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (gSocket == INVALID_SOCKET) { + wprintf(L"%s socket failed with error %d\n", NETWORKTYPESTR, WSAGetLastError()); + return; + } + + // Set non-blocking mode + u_long iMode = 1; + rc = ioctlsocket(gSocket, FIONBIO, &iMode); + if (rc != NO_ERROR) { + printf("%s ioctlsocket failed with error: %ld\n", NETWORKTYPESTR, rc); + } + + if (networkType == NT_SERVER) { + //----------------------------------------------- + // Bind the socket to any address and the specified port. + struct sockaddr_in rxAddr; + rxAddr.sin_family = AF_INET; + rxAddr.sin_port = htons(27015); + rxAddr.sin_addr.s_addr = htonl(INADDR_ANY); + rc = bind(gSocket, (SOCKADDR *)& rxAddr, sizeof(rxAddr)); + if (rc != 0) { + wprintf(L"%s bind failed with error %d\n", NETWORKTYPESTR, WSAGetLastError()); + return; + } + } + else if (networkType == NT_CLIENT) { + txPort = htons(27015); + } +} + +void network_track(struct MarioState *inMarioStates) { + marioStates = inMarioStates; +} + +void network_send(char* buffer, int buffer_length) { + if (networkType == NT_NONE) { return; } + struct sockaddr_in txAddr; + txAddr.sin_family = AF_INET; + txAddr.sin_port = txPort; + txAddr.sin_addr.s_addr = inet_addr(txIpAddr); + + int rc = sendto(gSocket, buffer, buffer_length, 0, (SOCKADDR *)& txAddr, sizeof(txAddr)); + if (rc == SOCKET_ERROR) { + wprintf(L"%s sendto failed with error: %d\n", NETWORKTYPESTR, WSAGetLastError()); + closesocket(gSocket); + WSACleanup(); + return; + } +} + +void network_update(void) { + if (networkType == NT_NONE) { return; } + char buffer[BUFFER_LENGTH]; + struct sockaddr_in rxAddr; + int rxAddrSize = sizeof(rxAddr); + + if (marioStates != NULL) { + memcpy(&buffer[0], &marioStates[0], 96); + memcpy(&buffer[96], marioStates[0].controller, 20); + memcpy(&buffer[96 + 20], marioStates[0].marioObj->rawData.asU32, 320); + network_send(buffer, 96 + 20 + 320); + } + + do { + int rc = recvfrom(gSocket, buffer, BUFFER_LENGTH, 0, (SOCKADDR *)&rxAddr, &rxAddrSize); + if (rc == SOCKET_ERROR) { + int error = WSAGetLastError(); + if (error != WSAEWOULDBLOCK && error != WSAECONNRESET) { + wprintf(L"%s recvfrom failed with error %d\n", NETWORKTYPESTR, WSAGetLastError()); + } + return; + } + if (networkType == NT_SERVER) { txPort = rxAddr.sin_port; } + + if (rc != 96 + 20 + 320) { + printf("%s received error: %s\n", NETWORKTYPESTR, rc); + break; + } + else if (marioStates != NULL) { + int oldActionState = marioStates[1].actionState; + memcpy(&marioStates[1], &buffer[0], 96); + memcpy(marioStates[1].controller, &buffer[96], 20); + memcpy(&marioStates[1].marioObj->rawData.asU32, &buffer[96 + 20], 320); + marioStates[1].actionState = oldActionState; + } + } while (1); + +} + +void network_shutdown(void) { + if (networkType == NT_NONE) { return; } + int rc = closesocket(gSocket); + if (rc == SOCKET_ERROR) { + wprintf(L"%s closesocket failed with error %d\n", NETWORKTYPESTR, WSAGetLastError()); + } + WSACleanup(); +} \ No newline at end of file diff --git a/src/pc/network/network.h b/src/pc/network/network.h new file mode 100644 index 00000000..76a13b60 --- /dev/null +++ b/src/pc/network/network.h @@ -0,0 +1,13 @@ +#ifndef NETWORK_H +#define NETWORK_H + +#include +#include "../cliopts.h" + +void network_init(enum NetworkType networkType); +void network_track(struct MarioState *marioStates); +void network_send(char* buffer, int buffer_length); +void network_update(void); +void network_shutdown(void); + +#endif diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index 47c461b3..30e082c8 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -11,6 +11,8 @@ #include "game/memory.h" #include "audio/external.h" +#include "network/network.h" + #include "gfx/gfx_pc.h" #include "gfx/gfx_opengl.h" @@ -103,6 +105,7 @@ static inline void patch_interpolations(void) { } void produce_one_frame(void) { + network_update(); gfx_start_frame(); const f32 master_mod = (f32)configMasterVolume / 127.0f; @@ -151,6 +154,7 @@ void game_deinit(void) { controller_shutdown(); audio_shutdown(); gfx_shutdown(); + network_shutdown(); inited = false; } @@ -253,6 +257,7 @@ void main_func(void) { audio_api = &audio_null; } + network_init(gCLIOpts.Network); audio_init(); sound_init();