diff --git a/Makefile b/Makefile index 26e3667d..84225e7c 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/pc/network +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 src/pc/network/packets ASM_DIRS := ifeq ($(DISCORDRPC),1) diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj index 389ef6e0..eb32f1ae 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj +++ b/build-windows-visual-studio/sm64ex.vcxproj @@ -3943,7 +3943,7 @@ - + @@ -3979,6 +3979,7 @@ + @@ -4282,8 +4283,7 @@ - - + diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters index 694908d9..16d1ef8b 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj.filters +++ b/build-windows-visual-studio/sm64ex.vcxproj.filters @@ -3418,6 +3418,9 @@ {1c96986a-a883-42fa-adb0-1f0998af2961} + + {f7dcd9e2-f84f-4c82-96c5-0dc32d408693} + @@ -14937,9 +14940,12 @@ Source Files\actors\mario - + Source Files\src\pc\network + + Source Files\src\pc\network\packets + @@ -15841,10 +15847,7 @@ - - Header Files\src\pc\network - - + Header Files\src\pc\network diff --git a/include/object_fields.h b/include/object_fields.h index 9f2661ce..9e9998bf 100644 --- a/include/object_fields.h +++ b/include/object_fields.h @@ -54,7 +54,7 @@ #define /*0x090*/ oDialogResponse OBJECT_FIELD_S16(0x02, 0) #define /*0x092*/ oDialogState OBJECT_FIELD_S16(0x02, 1) #define /*0x094*/ oUnk94 OBJECT_FIELD_U32(0x03) -// 0x98 unused/removed. +#define /*0x098*/ oSyncID OBJECT_FIELD_U32(0x04) #define /*0x09C*/ oIntangibleTimer OBJECT_FIELD_S32(0x05) #define /*0x0A0*/ O_POS_INDEX 0x06 #define /*0x0A0*/ oPosX OBJECT_FIELD_F32(O_POS_INDEX + 0) diff --git a/levels/castle_grounds/script.c b/levels/castle_grounds/script.c index 63c5ba48..374c5fd6 100644 --- a/levels/castle_grounds/script.c +++ b/levels/castle_grounds/script.c @@ -90,7 +90,7 @@ 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_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/src/game/behaviors/bobomb.inc.c b/src/game/behaviors/bobomb.inc.c index a0fde4d2..75466020 100644 --- a/src/game/behaviors/bobomb.inc.c +++ b/src/game/behaviors/bobomb.inc.c @@ -17,6 +17,7 @@ void bhv_bobomb_init(void) { o->oFriction = 0.8; o->oBuoyancy = 1.3; o->oInteractionSubtype = INT_SUBTYPE_KICKABLE; + network_init_object(o); } void bobomb_spawn_coin(void) { @@ -139,7 +140,7 @@ void generic_bobomb_free_loop(void) { bobomb_check_interactions(); if (o->oBobombFuseTimer >= 151) - o->oAction = 3; + o->oAction = BOBOMB_ACT_EXPLODE; } void stationary_bobomb_free_loop(void) { diff --git a/src/game/mario.c b/src/game/mario.c index 072fa1f5..16c5b7d3 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -39,8 +39,6 @@ #include "bettercamera.h" #endif -#include "../pc/network/network.h" - u32 unused80339F10; s8 filler80339F1C[20]; @@ -1940,8 +1938,6 @@ skippy: init_mario(); gMarioState = &gMarioStates[0]; } - - network_track(gMarioStates); } void init_mario_from_save_file(void) { diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index 2430beaf..ec61c714 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -31,6 +31,7 @@ #include "save_file.h" #include "spawn_object.h" #include "spawn_sound.h" +#include "pc/network/network.h" /** * @file obj_behaviors.c diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 54d5d83c..878ad0af 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -1,30 +1,20 @@ +#include +#include "network.h" +#include "object_fields.h" +#include "object_constants.h" + +// Winsock includes +#include +#include +//#pragma comment(lib, "Ws2_32.lib") + ////////////////////////// // 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; @@ -53,78 +43,62 @@ void network_init(enum NetworkType inNetworkType) { 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); + // Bind the socket to any address and the specified port. + struct sockaddr_in rxAddr; + rxAddr.sin_family = AF_INET; + rxAddr.sin_port = htons(networkType == NT_SERVER ? 27015 : 27016); + 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; } + + // Save the port to send to + txPort = htons(networkType == NT_SERVER ? 27016 : 27015); } -void network_track(struct MarioState *inMarioStates) { - marioStates = inMarioStates; -} - -void network_send(char* buffer, int buffer_length) { +void network_send(struct Packet* p) { if (networkType == NT_NONE) { return; } + if (p->error) { printf("%s packet error!\n", NETWORKTYPESTR); return; } struct sockaddr_in txAddr; txAddr.sin_family = AF_INET; txAddr.sin_port = txPort; - txAddr.sin_addr.s_addr = inet_addr(txIpAddr); + txAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); - int rc = sendto(gSocket, buffer, buffer_length, 0, (SOCKADDR *)& txAddr, sizeof(txAddr)); + int rc = sendto(gSocket, p->buffer, p->cursor, 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); - } + network_send_player(); + network_send_object(NULL); do { - int rc = recvfrom(gSocket, buffer, BUFFER_LENGTH, 0, (SOCKADDR *)&rxAddr, &rxAddrSize); + struct sockaddr_in rxAddr; + int rxAddrSize = sizeof(rxAddr); + struct Packet p = { .cursor = 1 }; + int rc = recvfrom(gSocket, p.buffer, PACKET_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; } + if (rc == 0) { break; } + + switch (p.buffer[0]) { + case PACKET_PLAYER: network_receive_player(&p); break; + case PACKET_OBJECT: network_receive_object(&p); break; + default: printf("%s received unknown packet: %d\n", NETWORKTYPESTR, p.buffer[0]); + } + } while (1); } diff --git a/src/pc/network/network.h b/src/pc/network/network.h index 76a13b60..72a98539 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -4,10 +4,39 @@ #include #include "../cliopts.h" +#define PACKET_LENGTH 1024 +#define NETWORKTYPESTR (networkType == NT_CLIENT ? "Client" : "Server") + +extern struct MarioState gMarioStates[]; +extern enum NetworkType networkType; + +enum PacketType { + PACKET_PLAYER, + PACKET_OBJECT +}; + +struct Packet { + int cursor; + bool error; + char buffer[PACKET_LENGTH]; +}; + void network_init(enum NetworkType networkType); -void network_track(struct MarioState *marioStates); -void network_send(char* buffer, int buffer_length); +void network_init_object(struct Object *object); +void network_send(struct Packet* p); void network_update(void); void network_shutdown(void); +// packet read / write +void packet_init(struct Packet* packet, enum PacketType packetType); +void packet_write(struct Packet* packet, void* data, int length); +void packet_read(struct Packet* packet, void* data, int length); + +// packet headers +void network_send_player(void); +void network_receive_player(struct Packet* p); + +void network_send_object(struct Object* o); +void network_receive_object(struct Packet* p); + #endif diff --git a/src/pc/network/packets/packet_object.c b/src/pc/network/packets/packet_object.c new file mode 100644 index 00000000..70bb1732 --- /dev/null +++ b/src/pc/network/packets/packet_object.c @@ -0,0 +1,50 @@ +#include +#include "../network.h" +#include "object_fields.h" +#include "object_constants.h" + +u32 nextSyncID = 1; +struct Object* syncObject = NULL; + +float player_distance(struct MarioState* marioState, struct Object* obj) { + if (marioState->marioObj == NULL) { return 0; } + f32 mx = marioState->marioObj->header.gfx.pos[0] - obj->oPosX; + f32 my = marioState->marioObj->header.gfx.pos[1] - obj->oPosY; + f32 mz = marioState->marioObj->header.gfx.pos[2] - obj->oPosZ; + mx *= mx; + my *= my; + mz *= mz; + return sqrt(mx + my + mz); +} + +void network_init_object(struct Object *object) { + object->oSyncID = nextSyncID++; + syncObject = object; +} + +void network_send_object(struct Object* o) { + o = syncObject; + if (o == NULL) { return; } + if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { return; } + if (player_distance(&gMarioStates[0], o) > player_distance(&gMarioStates[1], o)) { return; } + + struct Packet p; + packet_init(&p, PACKET_OBJECT); + packet_write(&p, &o->oSyncID, 4); + packet_write(&p, &o->oPosX, 28); + packet_write(&p, &o->oAction, 4); + packet_write(&p, &o->oHeldState, 4); + packet_write(&p, &o->oMoveAngleYaw, 4); + + network_send(&p); +} + +void network_receive_object(struct Packet* p) { + if (syncObject == NULL) { return; } + u32 syncId; + packet_read(p, &syncId, 4); + packet_read(p, &syncObject->oPosX, 28); + packet_read(p, &syncObject->oAction, 4); + packet_read(p, &syncObject->oHeldState, 4); + packet_read(p, &syncObject->oMoveAngleYaw, 4); +} diff --git a/src/pc/network/packets/packet_player.c b/src/pc/network/packets/packet_player.c new file mode 100644 index 00000000..795eed86 --- /dev/null +++ b/src/pc/network/packets/packet_player.c @@ -0,0 +1,24 @@ +#include +#include "../network.h" + +void network_send_player(void) { + if (gMarioStates[0].marioObj == NULL) { return; } + struct Packet p; + packet_init(&p, PACKET_PLAYER); + packet_write(&p, &gMarioStates[0], 96); + packet_write(&p, gMarioStates[0].controller, 20); + packet_write(&p, gMarioStates[0].marioObj->rawData.asU32, 320); + network_send(&p); +} + +void network_receive_player(struct Packet* p) { + if (gMarioStates[1].marioObj == NULL) { return; } + int oldActionState = gMarioStates[1].actionState; + + packet_read(p, &gMarioStates[1], 96); + packet_read(p, gMarioStates[1].controller, 20); + packet_read(p, &gMarioStates[1].marioObj->rawData.asU32, 320); + + // restore action state, needed for jump kicking + gMarioStates[1].actionState = oldActionState; +} diff --git a/src/pc/network/packets/packet_read_write.c b/src/pc/network/packets/packet_read_write.c new file mode 100644 index 00000000..c42e767e --- /dev/null +++ b/src/pc/network/packets/packet_read_write.c @@ -0,0 +1,20 @@ +#include "../network.h" + +void packet_init(struct Packet* packet, enum PacketType packetType) { + memset(packet->buffer, 0, PACKET_LENGTH); + packet->buffer[0] = (char)packetType; + packet->cursor = 1; + packet->error = false; +} + +void packet_write(struct Packet* packet, void* data, int length) { + if (data == NULL) { packet->error = true; return; } + memcpy(&packet->buffer[packet->cursor], data, length); + packet->cursor += length; +} + +void packet_read(struct Packet* packet, void* data, int length) { + if (data == NULL) { packet->error = true; return; } + memcpy(data, &packet->buffer[packet->cursor], length); + packet->cursor += length; +}