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;
+}