mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-10-18 19:22:41 +00:00
Rewrote how RNG is synchronized
The old implementation was hacky and often returned desynchronized results. This implementation gives a seed to each sync object, and synchronizes that seed. Also allows for a seed to be saved temporarily based on position and recycled for multiple calls.
This commit is contained in:
parent
87074ef098
commit
b831acd59c
22 changed files with 116 additions and 56 deletions
|
@ -3883,6 +3883,7 @@
|
|||
<ClCompile Include="..\src\game\print.c" />
|
||||
<ClCompile Include="..\src\game\profiler.c" />
|
||||
<ClCompile Include="..\src\game\rendering_graph_node.c" />
|
||||
<ClCompile Include="..\src\game\rng_position.c" />
|
||||
<ClCompile Include="..\src\game\save_file.c" />
|
||||
<ClCompile Include="..\src\game\screen_transition.c" />
|
||||
<ClCompile Include="..\src\game\shadow.c" />
|
||||
|
@ -4324,6 +4325,7 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include="..\include\behavior_table.h" />
|
||||
<ClInclude Include="..\src\game\chat.h" />
|
||||
<ClInclude Include="..\src\game\rng_position.h" />
|
||||
<ClInclude Include="..\src\menu\custom_menu.h" />
|
||||
<ClInclude Include="..\src\menu\custom_menu_system.h" />
|
||||
<ClInclude Include="..\src\pc\controller\controller_keyboard_debug.h" />
|
||||
|
|
|
@ -15075,6 +15075,9 @@
|
|||
<ClCompile Include="..\src\pc\utils\string_linked_list.c">
|
||||
<Filter>Source Files\src\pc\utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\game\rng_position.c">
|
||||
<Filter>Source Files\src\game</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\actors\common0.h">
|
||||
|
@ -16030,5 +16033,8 @@
|
|||
<ClInclude Include="..\src\pc\utils\string_linked_list.h">
|
||||
<Filter>Source Files\src\pc\utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\game\rng_position.h">
|
||||
<Filter>Source Files\src\game</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -14,6 +14,7 @@
|
|||
#include "graph_node.h"
|
||||
#include "surface_collision.h"
|
||||
#include "pc/network/network.h"
|
||||
#include "game/rng_position.h"
|
||||
|
||||
// Macros for retrieving arguments from behavior scripts.
|
||||
#define BHV_CMD_GET_1ST_U8(index) (u8)((gCurBhvCommand[index] >> 24) & 0xFF) // unused
|
||||
|
@ -30,7 +31,6 @@
|
|||
#define BHV_CMD_GET_ADDR_OF_CMD(index) (uintptr_t)(&gCurBhvCommand[index])
|
||||
|
||||
static u16 gRandomSeed16;
|
||||
static u16 gSavedSeed16;
|
||||
|
||||
// Unused function that directly jumps to a behavior command and resets the object's stack index.
|
||||
static void goto_behavior_unused(const BehaviorScript *bhvAddr) {
|
||||
|
@ -38,44 +38,21 @@ static void goto_behavior_unused(const BehaviorScript *bhvAddr) {
|
|||
gCurrentObject->bhvStackIndex = 0;
|
||||
}
|
||||
|
||||
void force_replicable_seed(u8 always) {
|
||||
// force the seed to consistent values
|
||||
extern u32 gGlobalTimer;
|
||||
static u32 lastTimer = 0;
|
||||
static f32 lastPos[3] = { 0 };
|
||||
if (gGlobalTimer == lastTimer
|
||||
&& lastPos[0] == gCurrentObject->oPosX / 10
|
||||
&& lastPos[1] == gCurrentObject->oPosY / 10
|
||||
&& lastPos[2] == gCurrentObject->oPosZ / 10
|
||||
&& !always) {
|
||||
gSavedSeed16 = 0;
|
||||
return;
|
||||
}
|
||||
gRandomSeed16 = (u16)(gCurrentObject->oPosX / 1000.0f)
|
||||
^ (u16)(gCurrentObject->oPosY / 1000.0f)
|
||||
^ (u16)(gCurrentObject->oPosZ / 1000.0f);
|
||||
if (!always) {
|
||||
lastPos[0] = gCurrentObject->oPosX / 10;
|
||||
lastPos[1] = gCurrentObject->oPosY / 10;
|
||||
lastPos[2] = gCurrentObject->oPosZ / 10;
|
||||
lastTimer = gGlobalTimer;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a pseudorandom integer from 0 to 65535 from the random seed, and update the seed.
|
||||
u16 random_u16(void) {
|
||||
// restore random seed when applicable
|
||||
if (gSavedSeed16 != 0) {
|
||||
gRandomSeed16 = gSavedSeed16;
|
||||
gSavedSeed16 = 0;
|
||||
}
|
||||
u16 savedSeed = gRandomSeed16;
|
||||
struct SyncObject* so = NULL;
|
||||
|
||||
// override this function for synchronized entities
|
||||
if (gCurrentObject->oSyncID != 0) {
|
||||
struct SyncObject* so = &gSyncObjects[gCurrentObject->oSyncID];
|
||||
if (so->o != NULL && !so->keepRandomSeed) {
|
||||
gSavedSeed16 = gRandomSeed16;
|
||||
force_replicable_seed(FALSE);
|
||||
if (gOverrideRngPosition != NULL) {
|
||||
// override this function for rng positions
|
||||
gRandomSeed16 = gOverrideRngPosition->seed;
|
||||
} else if (gCurrentObject->oSyncID != 0) {
|
||||
// override this function for synchronized entities
|
||||
so = &gSyncObjects[gCurrentObject->oSyncID];
|
||||
if (so->o == gCurrentObject) {
|
||||
gRandomSeed16 = so->randomSeed;
|
||||
} else {
|
||||
so = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,6 +80,17 @@ u16 random_u16(void) {
|
|||
gRandomSeed16 = temp2 ^ 0x8180;
|
||||
}
|
||||
|
||||
// restore seed
|
||||
if (gOverrideRngPosition != NULL) {
|
||||
gOverrideRngPosition->seed = gRandomSeed16;
|
||||
gRandomSeed16 = savedSeed;
|
||||
return gOverrideRngPosition->seed;
|
||||
} else if (so != NULL) {
|
||||
so->randomSeed = gRandomSeed16;
|
||||
gRandomSeed16 = savedSeed;
|
||||
return so->randomSeed;
|
||||
}
|
||||
|
||||
return gRandomSeed16;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
#define obj_and_int(object, offset, value) object->OBJECT_FIELD_S32(offset) &= (s32)(value)
|
||||
|
||||
void force_replicable_seed(u8 always);
|
||||
u16 random_u16(void);
|
||||
float random_float(void);
|
||||
s32 random_sign(void);
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "spawn_sound.h"
|
||||
#include "thread6.h"
|
||||
#include "area.h"
|
||||
#include "game/rng_position.h"
|
||||
|
||||
#define o gCurrentObject
|
||||
|
||||
|
|
|
@ -182,7 +182,6 @@ void bhv_generic_bowling_ball_spawner_init(void) {
|
|||
void bhv_generic_bowling_ball_spawner_loop(void) {
|
||||
if (!network_sync_object_initialized(o)) {
|
||||
struct SyncObject* so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS);
|
||||
so->keepRandomSeed = TRUE;
|
||||
}
|
||||
|
||||
struct Object *bowlingBall;
|
||||
|
@ -219,7 +218,6 @@ void bhv_generic_bowling_ball_spawner_loop(void) {
|
|||
void bhv_thi_bowling_ball_spawner_loop(void) {
|
||||
if (!network_sync_object_initialized(o)) {
|
||||
struct SyncObject* so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS);
|
||||
so->keepRandomSeed = TRUE;
|
||||
}
|
||||
|
||||
struct Object *bowlingBall;
|
||||
|
@ -255,7 +253,6 @@ void bhv_bob_pit_bowling_ball_init(void) {
|
|||
|
||||
struct SyncObject* so = network_init_object(o, 5000.0f);
|
||||
so->maxUpdateRate = 5.0f;
|
||||
so->keepRandomSeed = TRUE;
|
||||
}
|
||||
|
||||
void bhv_bob_pit_bowling_ball_loop(void) {
|
||||
|
|
|
@ -204,7 +204,6 @@ void bully_step(void) {
|
|||
}
|
||||
|
||||
void bully_spawn_coin(void) {
|
||||
force_replicable_seed(TRUE);
|
||||
struct Object *coin = spawn_object(o, MODEL_YELLOW_COIN, bhvMovingYellowCoin);
|
||||
#ifdef VERSION_JP //TODO: maybe move this ifdef logic to the header?
|
||||
cur_obj_play_sound_2(SOUND_GENERAL_COIN_SPURT);
|
||||
|
|
|
@ -8,7 +8,6 @@ void bhv_checkerboard_elevator_group_init(void) {
|
|||
struct SyncObject* so = network_init_object(o, 1000.0f);
|
||||
so->hasStandardFields = FALSE;
|
||||
so->maxUpdateRate = 5.0f;
|
||||
so->keepRandomSeed = TRUE;
|
||||
s32 sp3C;
|
||||
s32 sp38;
|
||||
s32 sp34;
|
||||
|
|
|
@ -49,10 +49,11 @@ void bhv_temp_coin_loop(void) {
|
|||
}
|
||||
|
||||
void bhv_coin_init(void) {
|
||||
force_replicable_seed(FALSE);
|
||||
rng_position_init(o->oPosX, o->oPosY, o->oPosZ);
|
||||
o->oVelY = random_float() * 10.0f + 30 + o->oCoinUnk110;
|
||||
o->oForwardVel = random_float() * 10.0f;
|
||||
o->oMoveAngleYaw = random_u16();
|
||||
rng_position_finish();
|
||||
cur_obj_set_behavior(bhvYellowCoin);
|
||||
obj_set_hitbox(o, &sYellowCoinHitbox);
|
||||
cur_obj_become_intangible();
|
||||
|
|
|
@ -24,7 +24,6 @@ void bhv_ship_part_3_loop(void) {
|
|||
if (!network_sync_object_initialized(o)) {
|
||||
struct SyncObject* so = network_init_object(o, 4000.0f);
|
||||
so->maxUpdateRate = 5.0f;
|
||||
so->keepRandomSeed = TRUE;
|
||||
network_init_object_field(o, &o->oFaceAnglePitch);
|
||||
network_init_object_field(o, &o->oFaceAngleRoll);
|
||||
network_init_object_field(o, &o->oShipPart3UnkF4);
|
||||
|
@ -46,7 +45,6 @@ void bhv_jrb_sliding_box_loop(void) {
|
|||
if (!network_sync_object_initialized(o)) {
|
||||
struct SyncObject* so = network_init_object(o, 4000.0f);
|
||||
so->maxUpdateRate = 5.0f;
|
||||
so->keepRandomSeed = TRUE;
|
||||
network_init_object_field(o, &o->oFaceAnglePitch);
|
||||
network_init_object_field(o, &o->oFaceAngleRoll);
|
||||
network_init_object_field(o, &o->oJrbSlidingBoxUnkF8);
|
||||
|
|
|
@ -17,7 +17,6 @@ void hexagonal_ring_spawn_flames(void) {
|
|||
void bhv_lll_rotating_hexagonal_ring_loop(void) {
|
||||
if (!network_sync_object_initialized(o)) {
|
||||
struct SyncObject* so = network_init_object(o, 4000.0f);
|
||||
so->keepRandomSeed = FALSE;
|
||||
network_init_object_field(o, &o->oAngleVelYaw);
|
||||
}
|
||||
UNUSED s32 unused;
|
||||
|
|
|
@ -57,7 +57,6 @@ void bhv_platform_on_track_init(void) {
|
|||
struct SyncObject* so = network_init_object(o, 1000.0f);
|
||||
so->fullObjectSync = TRUE;
|
||||
so->maxUpdateRate = 5.0f;
|
||||
so->keepRandomSeed = TRUE;
|
||||
}
|
||||
|
||||
if (!(o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM)) {
|
||||
|
|
|
@ -33,7 +33,6 @@ void bhv_water_bomb_spawner_update(void) {
|
|||
struct SyncObject* so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS);
|
||||
so->fullObjectSync = TRUE;
|
||||
so->maxUpdateRate = 5.0f;
|
||||
so->keepRandomSeed = TRUE;
|
||||
network_init_object_field(o, &o->oWaterBombSpawnerBombActive);
|
||||
network_init_object_field(o, &o->oWaterBombSpawnerTimeToSpawn);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ void bhv_yoshi_init(void) {
|
|||
struct SyncObject* so = network_init_object(o, 4000.0f);
|
||||
so->ignore_if_true = bhv_yoshi_ignore_if_true;
|
||||
so->override_ownership = bhv_yoshi_override_ownership;
|
||||
so->keepRandomSeed = TRUE;
|
||||
network_init_object_field(o, &o->oYoshiBlinkTimer);
|
||||
network_init_object_field(o, &o->oYoshiChosenHome);
|
||||
network_init_object_field(o, &o->oYoshiTargetYaw);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "segment2.h"
|
||||
#include "segment_symbols.h"
|
||||
#include "thread6.h"
|
||||
#include "rng_position.h"
|
||||
#include <prevent_bss_reordering.h>
|
||||
#ifdef BETTERCAMERA
|
||||
#include "bettercamera.h"
|
||||
|
@ -605,4 +606,7 @@ void game_loop_one_iteration(void) {
|
|||
// amount of free space remaining.
|
||||
print_text_fmt_int(180, 20, "BUF %d", gGfxPoolEnd - (u8 *) gDisplayListHead);
|
||||
}
|
||||
|
||||
// custom coop hooks
|
||||
rng_position_update();
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "spawn_object.h"
|
||||
#include "spawn_sound.h"
|
||||
#include "pc/network/network.h"
|
||||
#include "game/rng_position.h"
|
||||
|
||||
/**
|
||||
* @file obj_behaviors.c
|
||||
|
@ -668,14 +669,15 @@ s32 obj_find_wall_displacement(Vec3f dist, f32 x, f32 y, f32 z, f32 radius) {
|
|||
void obj_spawn_yellow_coins(struct Object *obj, s8 nCoins) {
|
||||
struct Object *coin;
|
||||
s8 count;
|
||||
force_replicable_seed(TRUE);
|
||||
|
||||
rng_position_init(o->oPosX, o->oPosY, o->oPosZ);
|
||||
for (count = 0; count < nCoins; count++) {
|
||||
coin = spawn_object(obj, MODEL_YELLOW_COIN, bhvMovingYellowCoin);
|
||||
coin->oForwardVel = random_float() * 20;
|
||||
coin->oVelY = random_float() * 40 + 20;
|
||||
coin->oMoveAngleYaw = random_u16();
|
||||
}
|
||||
rng_position_finish();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -745,7 +745,6 @@ static s32 obj_handle_attacks(struct ObjectHitbox *hitbox, s32 attackedMarioActi
|
|||
break;
|
||||
|
||||
case ATTACK_HANDLER_SQUISHED_WITH_BLUE_COIN:
|
||||
force_replicable_seed(TRUE);
|
||||
o->oNumLootCoins = -1;
|
||||
obj_set_squished_action();
|
||||
break;
|
||||
|
|
|
@ -1705,7 +1705,6 @@ static void obj_spawn_loot_coins(struct Object *obj, s32 numCoins, f32 sp30,
|
|||
spawnHeight = obj->oPosY;
|
||||
}
|
||||
|
||||
force_replicable_seed(TRUE);
|
||||
for (i = 0; i < numCoins; i++) {
|
||||
if (obj->oNumLootCoins <= 0) {
|
||||
break;
|
||||
|
@ -1729,7 +1728,6 @@ void obj_spawn_loot_yellow_coins(struct Object *obj, s32 numCoins, f32 sp28) {
|
|||
}
|
||||
|
||||
void cur_obj_spawn_loot_coin_at_mario_pos(struct MarioState* m) {
|
||||
force_replicable_seed(TRUE);
|
||||
struct Object *coin;
|
||||
if (o->oNumLootCoins <= 0) {
|
||||
return;
|
||||
|
@ -3031,7 +3029,6 @@ s32 cur_obj_check_interacted(void) {
|
|||
}
|
||||
|
||||
void cur_obj_spawn_loot_blue_coin(void) {
|
||||
force_replicable_seed(TRUE);
|
||||
if (o->oNumLootCoins >= 5) {
|
||||
spawn_object(o, MODEL_BLUE_COIN, bhvMrIBlueCoin);
|
||||
o->oNumLootCoins -= 5;
|
||||
|
|
50
src/game/rng_position.c
Normal file
50
src/game/rng_position.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include "rng_position.h"
|
||||
#include "engine/math_util.h"
|
||||
|
||||
#define RNG_POSITION_MAX 16
|
||||
#define RNG_POSITION_MATCH_DIST 300
|
||||
#define RNG_POSITION_LIFE 30
|
||||
|
||||
struct RngPosition sRngPosition[RNG_POSITION_MAX] = { 0 };
|
||||
struct RngPosition* gOverrideRngPosition = NULL;
|
||||
|
||||
void rng_position_init(f32 x, f32 y, f32 z) {
|
||||
Vec3f position = { x, y, z };
|
||||
// try to find and update past rng position
|
||||
for (u8 i = 0; i < RNG_POSITION_MAX; i++) {
|
||||
if (sRngPosition[i].life == 0) { continue; }
|
||||
Vec3f difference = { 0 };
|
||||
vec3f_dif(difference, position, sRngPosition[i].position);
|
||||
f32 length = vec3f_length(difference);
|
||||
if (length >= RNG_POSITION_MATCH_DIST) { continue; }
|
||||
sRngPosition[i].life = RNG_POSITION_LIFE;
|
||||
gOverrideRngPosition = &sRngPosition[i];
|
||||
return;
|
||||
}
|
||||
|
||||
// try to store new rng position
|
||||
for (u8 i = 0; i < RNG_POSITION_MAX; i++) {
|
||||
if (sRngPosition[i].life != 0) { continue; }
|
||||
sRngPosition[i].life = RNG_POSITION_LIFE;
|
||||
sRngPosition[i].seed = (u16)((position[0] / (3 * RNG_POSITION_MATCH_DIST)) + (position[1] / (3 * RNG_POSITION_MATCH_DIST)));
|
||||
vec3f_copy(sRngPosition[i].position, position);
|
||||
gOverrideRngPosition = &sRngPosition[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void rng_position_finish(void) {
|
||||
gOverrideRngPosition = NULL;
|
||||
}
|
||||
|
||||
void rng_position_update(void) {
|
||||
// decrease life of all rng positions
|
||||
for (u8 i = 0; i < RNG_POSITION_MAX; i++) {
|
||||
if (sRngPosition[i].life == 0) { continue; }
|
||||
sRngPosition[i].life--;
|
||||
if (sRngPosition[i].life > 0) { continue; }
|
||||
if (&sRngPosition[i] == gOverrideRngPosition) {
|
||||
gOverrideRngPosition = NULL;
|
||||
}
|
||||
}
|
||||
}
|
18
src/game/rng_position.h
Normal file
18
src/game/rng_position.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef RNG_POSITION_H
|
||||
#define RNG_POSITION_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
struct RngPosition {
|
||||
Vec3f position;
|
||||
u16 seed;
|
||||
u8 life;
|
||||
};
|
||||
|
||||
extern struct RngPosition* gOverrideRngPosition;
|
||||
|
||||
void rng_position_init(f32 x, f32 y, f32 z);
|
||||
void rng_position_finish(void);
|
||||
void rng_position_update(void);
|
||||
|
||||
#endif
|
|
@ -49,9 +49,9 @@ struct SyncObject {
|
|||
void* behavior;
|
||||
u16 txEventId;
|
||||
u16 rxEventId;
|
||||
u16 randomSeed;
|
||||
u8 extraFieldCount;
|
||||
bool fullObjectSync;
|
||||
bool keepRandomSeed;
|
||||
bool syncDeathEvent;
|
||||
bool hasStandardFields;
|
||||
float minUpdateRate;
|
||||
|
|
|
@ -63,7 +63,6 @@ struct SyncObject* network_init_object(struct Object *o, float maxSyncDistance)
|
|||
so->rxEventId = 0;
|
||||
so->txEventId = 0;
|
||||
so->fullObjectSync = false;
|
||||
so->keepRandomSeed = false;
|
||||
so->hasStandardFields = (maxSyncDistance >= 0);
|
||||
so->minUpdateRate = 0.33f;
|
||||
so->maxUpdateRate = 0.00f;
|
||||
|
@ -71,6 +70,7 @@ struct SyncObject* network_init_object(struct Object *o, float maxSyncDistance)
|
|||
so->on_received_pre = NULL;
|
||||
so->on_received_post = NULL;
|
||||
so->syncDeathEvent = true;
|
||||
so->randomSeed = (u16)(o->oSyncID * 7951);
|
||||
memset(so->extraFields, 0, sizeof(void*) * MAX_SYNC_OBJECT_FIELDS);
|
||||
|
||||
return so;
|
||||
|
@ -143,6 +143,7 @@ static void packet_write_object_header(struct Packet* p, struct Object* o) {
|
|||
|
||||
packet_write(p, &o->oSyncID, sizeof(u32));
|
||||
packet_write(p, &so->txEventId, sizeof(u16));
|
||||
packet_write(p, &so->randomSeed, sizeof(u16));
|
||||
packet_write(p, &behaviorId, sizeof(enum BehaviorId));
|
||||
}
|
||||
|
||||
|
@ -197,6 +198,9 @@ static struct SyncObject* packet_read_object_header(struct Packet* p) {
|
|||
}
|
||||
so->rxEventId = eventId;
|
||||
|
||||
// update the random seed
|
||||
packet_read(p, &so->randomSeed, sizeof(u16));
|
||||
|
||||
// make sure the behaviors match
|
||||
enum BehaviorId behaviorId;
|
||||
packet_read(p, &behaviorId, sizeof(enum BehaviorId));
|
||||
|
|
Loading…
Reference in a new issue