Synchronized chain chomp, adjustments to Koopa

Added entity event syncing system. Made sure old events don't override
new events.
This commit is contained in:
MysterD 2020-08-05 23:44:43 -07:00
parent 0a2c76c76e
commit 0a3b0623a5
8 changed files with 87 additions and 25 deletions

View file

@ -30,7 +30,7 @@ static struct ObjectHitbox sChainChompHitbox = {
void bhv_chain_chomp_chain_part_update(void) { void bhv_chain_chomp_chain_part_update(void) {
struct ChainSegment *segment; struct ChainSegment *segment;
if (o->parentObj->oAction == CHAIN_CHOMP_ACT_UNLOAD_CHAIN) { if (o->parentObj->behavior != &bhvChainChomp || o->parentObj->oAction == CHAIN_CHOMP_ACT_UNLOAD_CHAIN) {
obj_mark_for_deletion(o); obj_mark_for_deletion(o);
} else if (o->oBehParams2ndByte != CHAIN_CHOMP_CHAIN_PART_BP_PIVOT) { } else if (o->oBehParams2ndByte != CHAIN_CHOMP_CHAIN_PART_BP_PIVOT) {
segment = &o->parentObj->oChainChompSegments[o->oBehParams2ndByte]; segment = &o->parentObj->oChainChompSegments[o->oBehParams2ndByte];
@ -54,7 +54,9 @@ static void chain_chomp_act_uninitialized(void) {
s32 i; s32 i;
#ifndef NODRAWINGDISTANCE #ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 3000.0f) { struct Object* player = nearest_player_to_object(o);
int distanceToPlayer = dist_between_objects(o, player);
if (distanceToPlayer < 3000.0f) {
#endif #endif
segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment)); segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment));
if (segments != NULL) { if (segments != NULL) {
@ -177,9 +179,13 @@ static void chain_chomp_sub_act_turn(void) {
chain_chomp_restore_normal_chain_lengths(); chain_chomp_restore_normal_chain_lengths();
obj_move_pitch_approach(0, 0x100); obj_move_pitch_approach(0, 0x100);
struct Object* player = nearest_player_to_object(o);
int distanceToPlayer = dist_between_objects(o, player);
int angleToPlayer = obj_angle_to_object(o, player);
if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) { if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {
cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x400); cur_obj_rotate_yaw_toward(angleToPlayer, 0x400);
if (abs_angle_diff(o->oAngleToMario, o->oMoveAngleYaw) < 0x800) { if (abs_angle_diff(angleToPlayer, o->oMoveAngleYaw) < 0x800 && distanceToPlayer < 3000) {
if (o->oTimer > 30) { if (o->oTimer > 30) {
if (cur_obj_check_anim_frame(0)) { if (cur_obj_check_anim_frame(0)) {
cur_obj_reverse_animation(); cur_obj_reverse_animation();
@ -208,7 +214,7 @@ static void chain_chomp_sub_act_turn(void) {
o->oVelY = 20.0f; o->oVelY = 20.0f;
} }
} else { } else {
cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x190); cur_obj_rotate_yaw_toward(angleToPlayer, 0x190);
o->oTimer = 0; o->oTimer = 0;
} }
} }
@ -280,9 +286,12 @@ static void chain_chomp_released_lunge_around(void) {
// Finish bounce // Finish bounce
if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) { if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {
struct Object* player = nearest_player_to_object(o);
int angleToPlayer = obj_angle_to_object(o, player);
// Before first bounce, turn toward mario and wait 2 seconds // Before first bounce, turn toward mario and wait 2 seconds
if (o->oChainChompNumLunges == 0) { if (o->oChainChompNumLunges == 0) {
if (cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x320)) { if (cur_obj_rotate_yaw_toward(angleToPlayer, 0x320)) {
if (o->oTimer > 60) { if (o->oTimer > 60) {
o->oChainChompNumLunges += 1; o->oChainChompNumLunges += 1;
// enable wall collision // enable wall collision
@ -294,7 +303,7 @@ static void chain_chomp_released_lunge_around(void) {
} else { } else {
if (++o->oChainChompNumLunges <= 5) { if (++o->oChainChompNumLunges <= 5) {
cur_obj_play_sound_2(SOUND_GENERAL_CHAIN_CHOMP1); cur_obj_play_sound_2(SOUND_GENERAL_CHAIN_CHOMP1);
o->oMoveAngleYaw = o->oAngleToMario + random_sign() * 0x2000; o->oMoveAngleYaw = angleToPlayer + random_sign() * 0x2000;
o->oForwardVel = 30.0f; o->oForwardVel = 30.0f;
o->oVelY = 50.0f; o->oVelY = 50.0f;
} else { } else {
@ -364,7 +373,9 @@ static void chain_chomp_act_move(void) {
// Unload chain if mario is far enough // Unload chain if mario is far enough
#ifndef NODRAWINGDISTANCE #ifndef NODRAWINGDISTANCE
if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f) { struct Object* player = nearest_player_to_object(o);
int distanceToPlayer = dist_between_objects(o, player);
if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && distanceToPlayer > 4000.0f) {
o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN; o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN;
o->oForwardVel = o->oVelY = 0.0f; o->oForwardVel = o->oVelY = 0.0f;
} else { } else {
@ -468,7 +479,11 @@ static void chain_chomp_act_unload_chain(void) {
o->oAction = CHAIN_CHOMP_ACT_UNINITIALIZED; o->oAction = CHAIN_CHOMP_ACT_UNINITIALIZED;
if (o->oChainChompReleaseStatus != CHAIN_CHOMP_NOT_RELEASED) { if (o->oChainChompReleaseStatus != CHAIN_CHOMP_NOT_RELEASED) {
for (u8 i = 0; i < 5; i++) {
obj_mark_for_deletion(&o->oChainChompSegments[i]);
}
obj_mark_for_deletion(o); obj_mark_for_deletion(o);
obj_mark_for_deletion(o->parentObj);
} }
} }
@ -476,6 +491,10 @@ static void chain_chomp_act_unload_chain(void) {
* Update function for chain chomp. * Update function for chain chomp.
*/ */
void bhv_chain_chomp_update(void) { void bhv_chain_chomp_update(void) {
if (o->oSyncID == 0) {
network_init_object(o, 1000.0f);
}
switch (o->oAction) { switch (o->oAction) {
case CHAIN_CHOMP_ACT_UNINITIALIZED: case CHAIN_CHOMP_ACT_UNINITIALIZED:
chain_chomp_act_uninitialized(); chain_chomp_act_uninitialized();
@ -493,11 +512,21 @@ void bhv_chain_chomp_update(void) {
* Update function for wooden post. * Update function for wooden post.
*/ */
void bhv_wooden_post_update(void) { void bhv_wooden_post_update(void) {
if (o->oSyncID == 0) {
network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS);
network_init_object_field(o, &o->oWoodenPostMarioPounding);
network_init_object_field(o, &o->oWoodenPostOffsetY);
network_init_object_field(o, &o->oWoodenPostSpeedY);
network_init_object_field(o, &o->oWoodenPostTotalMarioAngle);
network_init_object_field(o, &o->oTimer);
}
// When ground pounded by mario, drop by -45 + -20 // When ground pounded by mario, drop by -45 + -20
if (!o->oWoodenPostMarioPounding) { if (!o->oWoodenPostMarioPounding) {
if ((o->oWoodenPostMarioPounding = cur_obj_is_mario_ground_pounding_platform())) { if ((o->oWoodenPostMarioPounding = cur_obj_is_mario_ground_pounding_platform())) {
cur_obj_play_sound_2(SOUND_GENERAL_POUND_WOOD_POST); cur_obj_play_sound_2(SOUND_GENERAL_POUND_WOOD_POST);
o->oWoodenPostSpeedY = -70.0f; o->oWoodenPostSpeedY = -70.0f;
if (network_owns_object(o)) { network_send_object(o); }
} }
} else if (approach_f32_ptr(&o->oWoodenPostSpeedY, 0.0f, 25.0f)) { } else if (approach_f32_ptr(&o->oWoodenPostSpeedY, 0.0f, 25.0f)) {
// Stay still until mario is done ground pounding // Stay still until mario is done ground pounding
@ -516,20 +545,25 @@ void bhv_wooden_post_update(void) {
if (o->oWoodenPostOffsetY != 0.0f) { if (o->oWoodenPostOffsetY != 0.0f) {
o->oPosY = o->oHomeY + o->oWoodenPostOffsetY; o->oPosY = o->oHomeY + o->oWoodenPostOffsetY;
} else if (!(o->oBehParams & WOODEN_POST_BP_NO_COINS_MASK)) { } else if (!(o->oBehParams & WOODEN_POST_BP_NO_COINS_MASK)) {
struct Object* player = nearest_player_to_object(o);
int distanceToPlayer = dist_between_objects(o, player);
int angleToPlayer = obj_angle_to_object(o, player);
// Reset the timer once mario is far enough // Reset the timer once mario is far enough
if (o->oDistanceToMario > 400.0f) { if (distanceToPlayer > 400.0f) {
o->oTimer = o->oWoodenPostTotalMarioAngle = 0; o->oTimer = o->oWoodenPostTotalMarioAngle = 0;
} else { } else {
// When mario runs around the post 3 times within 200 frames, spawn // When mario runs around the post 3 times within 200 frames, spawn
// coins // coins
o->oWoodenPostTotalMarioAngle += (s16)(o->oAngleToMario - o->oWoodenPostPrevAngleToMario); o->oWoodenPostTotalMarioAngle += (s16)(angleToPlayer - o->oWoodenPostPrevAngleToMario);
if (absi(o->oWoodenPostTotalMarioAngle) > 0x30000 && o->oTimer < 200) { if (absi(o->oWoodenPostTotalMarioAngle) > 0x30000 && o->oTimer < 200) {
if (network_owns_object(o)) { network_send_object(o); }
obj_spawn_loot_yellow_coins(o, 5, 20.0f); obj_spawn_loot_yellow_coins(o, 5, 20.0f);
set_object_respawn_info_bits(o, 1); set_object_respawn_info_bits(o, 1);
} }
} }
o->oWoodenPostPrevAngleToMario = o->oAngleToMario; o->oWoodenPostPrevAngleToMario = angleToPlayer;
} }
} }

View file

@ -86,10 +86,10 @@ void bhv_koopa_init(void) {
} }
network_init_object(o, 4000.0f); network_init_object(o, 4000.0f);
network_init_object_field(o, &o->oSubAction);
network_init_object_field(o, &o->oKoopaTargetYaw); network_init_object_field(o, &o->oKoopaTargetYaw);
network_init_object_field(o, &o->oKoopaCountdown); network_init_object_field(o, &o->oKoopaCountdown);
network_init_object_field(o, &o->oKoopaMovementType); network_init_object_field(o, &o->oKoopaMovementType);
network_init_object_field(o, &o->oKoopaUnshelledTimeUntilTurn);
} }
/** /**

View file

@ -54,8 +54,6 @@ void koopa_shell_spawn_sparkles(f32 a) {
void bhv_koopa_shell_loop(void) { void bhv_koopa_shell_loop(void) {
if (o->oSyncID == 0) { if (o->oSyncID == 0) {
network_init_object(o, 500.0f); network_init_object(o, 500.0f);
network_init_object_field(o, &o->oInteractStatus);
network_init_object_field(o, &o->oAction);
} }
struct Surface *sp34; struct Surface *sp34;

View file

@ -30,7 +30,6 @@ void breakable_box_init(void) {
break; break;
} }
network_init_object(o, SYNC_DISTANCE_ONLY_DEATH); network_init_object(o, SYNC_DISTANCE_ONLY_DEATH);
network_init_object_field(o, &o->oInteractStatus);
} }
void hidden_breakable_box_actions(void) { void hidden_breakable_box_actions(void) {

View file

@ -44,6 +44,7 @@
#include "save_file.h" #include "save_file.h"
#include "seq_ids.h" #include "seq_ids.h"
#include "spawn_sound.h" #include "spawn_sound.h"
#include "pc/network/network.h"
#define POS_OP_SAVE_POSITION 0 #define POS_OP_SAVE_POSITION 0
#define POS_OP_COMPUTE_VELOCITY 1 #define POS_OP_COMPUTE_VELOCITY 1

View file

@ -6,7 +6,8 @@
#include <assert.h> #include <assert.h>
#include "../cliopts.h" #include "../cliopts.h"
#define SYNC_DISTANCE_ONLY_DEATH -1 #define SYNC_DISTANCE_ONLY_DEATH -1.0f
#define SYNC_DISTANCE_ONLY_EVENTS -2.0f
#define SYNC_DISTANCE_INFINITE 0 #define SYNC_DISTANCE_INFINITE 0
#define MAX_SYNC_OBJECTS 256 #define MAX_SYNC_OBJECTS 256
#define MAX_SYNC_OBJECT_FIELDS 16 #define MAX_SYNC_OBJECT_FIELDS 16
@ -36,6 +37,7 @@ struct SyncObject {
bool owned; bool owned;
unsigned int ticksSinceUpdate; unsigned int ticksSinceUpdate;
void* behavior; void* behavior;
u16 onEventId;
u8 extraFieldCount; u8 extraFieldCount;
void* extraFields[MAX_SYNC_OBJECT_FIELDS]; void* extraFields[MAX_SYNC_OBJECT_FIELDS];
}; };
@ -66,6 +68,7 @@ void network_update_reliable(void);
void network_update_player(void); void network_update_player(void);
void network_receive_player(struct Packet* p); void network_receive_player(struct Packet* p);
bool network_owns_object(struct Object* o);
void network_update_objects(void); void network_update_objects(void);
void network_receive_object(struct Packet* p); void network_receive_object(struct Packet* p);

View file

@ -24,6 +24,7 @@ void network_init_object(struct Object *o, float maxSyncDistance) {
so->ticksSinceUpdate = -1; so->ticksSinceUpdate = -1;
so->extraFieldCount = 0; so->extraFieldCount = 0;
so->behavior = o->behavior; so->behavior = o->behavior;
so->onEventId = 0;
memset(so->extraFields, 0, sizeof(void*) * MAX_SYNC_OBJECT_FIELDS); memset(so->extraFields, 0, sizeof(void*) * MAX_SYNC_OBJECT_FIELDS);
} }
@ -34,18 +35,29 @@ void network_init_object_field(struct Object *o, void* field) {
so->extraFields[index] = field; so->extraFields[index] = field;
} }
bool network_owns_object(struct Object* o) {
struct SyncObject* so = &syncObjects[o->oSyncID];
if (so == NULL) { return false; }
return so->owned;
}
void network_send_object(struct Object* o) { void network_send_object(struct Object* o) {
struct SyncObject* so = &syncObjects[o->oSyncID]; struct SyncObject* so = &syncObjects[o->oSyncID];
if (so == NULL) { return; } if (so == NULL) { return; }
bool reliable = (o->activeFlags == ACTIVE_FLAG_DEACTIVATED); so->onEventId++;
bool reliable = (o->activeFlags == ACTIVE_FLAG_DEACTIVATED || so->maxSyncDistance == SYNC_DISTANCE_ONLY_EVENTS);
struct Packet p; struct Packet p;
packet_init(&p, PACKET_OBJECT, reliable); packet_init(&p, PACKET_OBJECT, reliable);
packet_write(&p, &o->oSyncID, 4); packet_write(&p, &o->oSyncID, 4);
packet_write(&p, &so->onEventId, 2);
packet_write(&p, &so->behavior, sizeof(void*)); packet_write(&p, &so->behavior, sizeof(void*));
packet_write(&p, &o->activeFlags, 2); packet_write(&p, &o->activeFlags, 2);
packet_write(&p, &o->oPosX, 28); packet_write(&p, &o->oPosX, 28);
packet_write(&p, &o->oAction, 4); packet_write(&p, &o->oAction, 4);
packet_write(&p, &o->oSubAction, 4);
packet_write(&p, &o->oInteractStatus, 4);
packet_write(&p, &o->oHeldState, 4); packet_write(&p, &o->oHeldState, 4);
packet_write(&p, &o->oMoveAngleYaw, 4); packet_write(&p, &o->oMoveAngleYaw, 4);
@ -61,6 +73,8 @@ void network_send_object(struct Object* o) {
if (o->behavior != so->behavior) { if (o->behavior != so->behavior) {
printf("network_send_object() BEHAVIOR MISMATCH!\n"); printf("network_send_object() BEHAVIOR MISMATCH!\n");
forget_sync_object(so);
return;
} }
network_send(&p); network_send(&p);
@ -85,6 +99,20 @@ void network_receive_object(struct Packet* p) {
return; return;
} }
// make sure this is the newest event possible
volatile u16 eventId = 0;
packet_read(p, &eventId, 2);
if (so->onEventId > eventId && (u16)abs(eventId - so->onEventId) < USHRT_MAX / 2) { return; }
so->onEventId = eventId;
// make sure the behaviors match
packet_read(p, &so->behavior, sizeof(void*));
if (o->behavior != so->behavior) {
printf("network_receive_object() BEHAVIOR MISMATCH!\n");
forget_sync_object(so);
return;
}
// sync only death // sync only death
if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_DEATH) { if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_DEATH) {
s16 activeFlags; s16 activeFlags;
@ -97,10 +125,11 @@ void network_receive_object(struct Packet* p) {
} }
// write object flags // write object flags
packet_read(p, &so->behavior, sizeof(void*));
packet_read(p, &o->activeFlags, 2); packet_read(p, &o->activeFlags, 2);
packet_read(p, &o->oPosX, 28); packet_read(p, &o->oPosX, 28);
packet_read(p, &o->oAction, 4); packet_read(p, &o->oAction, 4);
packet_read(p, &o->oSubAction, 4);
packet_read(p, &o->oInteractStatus, 4);
packet_read(p, &o->oHeldState, 4); packet_read(p, &o->oHeldState, 4);
packet_read(p, &o->oMoveAngleYaw, 4); packet_read(p, &o->oMoveAngleYaw, 4);
@ -113,10 +142,6 @@ void network_receive_object(struct Packet* p) {
packet_read(p, so->extraFields[i], 4); packet_read(p, so->extraFields[i], 4);
} }
if (o->behavior != so->behavior) {
printf("network_receive_object() BEHAVIOR MISMATCH!\n");
}
// deactivated // deactivated
if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) {
forget_sync_object(so); forget_sync_object(so);
@ -161,7 +186,8 @@ void network_update_objects(void) {
so->ticksSinceUpdate++; so->ticksSinceUpdate++;
// check if we should be the one syncing this object // check if we should be the one syncing this object
if (!should_own_object(so)) { continue; } so->owned = should_own_object(so);
if (!so->owned) { continue; }
// check update rate // check update rate
if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_DEATH) { if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_DEATH) {

View file

@ -24,6 +24,7 @@ void packet_write(struct Packet* packet, void* data, int length) {
void packet_read(struct Packet* packet, void* data, int length) { void packet_read(struct Packet* packet, void* data, int length) {
if (data == NULL) { packet->error = true; return; } if (data == NULL) { packet->error = true; return; }
memcpy(data, &packet->buffer[packet->cursor], length); int cursor = packet->cursor;
packet->cursor += length; memcpy(data, &packet->buffer[cursor], length);
packet->cursor = cursor + length;
} }