Skip to content

Commit c69a1aa

Browse files
authored
Merge pull request #7306 from notimaginative/multi_interp_fixes
Collection of fixes for multiple issues with the multi code found by Claude AI (by way of Goober5000), plus additional issues noted by myself while checking the code. These fixes are primarily focused on the interpolation code. These changes should not be breaking and are largely exclusive to server-side handling, but still need to be tested thoroughly before being merged. Any tests must be done with at least the server (master) using the fixes.
2 parents 41a8b18 + 3f26505 commit c69a1aa

8 files changed

Lines changed: 101 additions & 58 deletions

File tree

code/network/multi_interpolate.cpp

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ void interpolation_manager::reassess_packet_index(vec3d* pos, matrix* ori, physi
1414
int current_index = static_cast<int>(_packets.size()) - 2;
1515
int prev_index = static_cast<int>(_packets.size()) - 1;
1616

17+
const bool index_only = ( !pos || !ori || !pip );
18+
1719
// iterate through the packets
1820
for (; current_index > -1; current_index--, prev_index--) {
1921

@@ -25,7 +27,7 @@ void interpolation_manager::reassess_packet_index(vec3d* pos, matrix* ori, physi
2527
// probably the "hackiest" thing about this. If we were just straight simulating,
2628
// and we now need to go back, pretend that the position we were in *was* our old packet
2729
// and we are now going towards our new packet's physics.
28-
if (_simulation_mode) {
30+
if (_simulation_mode && !index_only) {
2931
replace_packet(prev_index, pos, ori, pip); // TODO, if simulation mode was forced by the collision code, this method regresses a bug where collisions instantly kill
3032
_simulation_mode = false;
3133
}
@@ -34,9 +36,11 @@ void interpolation_manager::reassess_packet_index(vec3d* pos, matrix* ori, physi
3436
}
3537
}
3638

37-
// if we didn't find indexes then we are overwhelmingly likely to have passed the server somehow
38-
// and we need to make sure that we just straight simulate these ships
39-
_simulation_mode = true;
39+
if ( !index_only ) {
40+
// if we didn't find indexes then we are overwhelmingly likely to have passed the server somehow
41+
// and we need to make sure that we just straight simulate these ships
42+
_simulation_mode = true;
43+
}
4044
}
4145

4246
void interpolate_main_helper(int objnum, vec3d* pos, matrix* ori, physics_info* pip, vec3d* last_pos, matrix* last_orient, vec3d* gravity, bool player_ship)
@@ -112,7 +116,7 @@ void interpolation_manager::interpolate_main(vec3d* pos, matrix* ori, physics_in
112116
}
113117

114118
// calc what the current timing should be.
115-
float numerator = static_cast<float>(_packets[_upcoming_packet_index].remote_missiontime) - static_cast<float>(Multi_Timing_Info.get_current_time());
119+
float numerator = static_cast<float>(Multi_Timing_Info.get_current_time()) - static_cast<float>(_packets[_prev_packet_index].remote_missiontime);
116120
float denominator = static_cast<float>(_packets[_upcoming_packet_index].remote_missiontime) - static_cast<float>(_packets[_prev_packet_index].remote_missiontime);
117121

118122
// work around for weird situations that might cause NAN (you just never know with multi)
@@ -162,7 +166,7 @@ void interpolation_manager::interpolate_main(vec3d* pos, matrix* ori, physics_in
162166
void interpolation_manager::reinterpolate_previous(TIMESTAMP stamp, int prev_packet_index, int next_packet_index, vec3d& position, matrix& orientation, vec3d& velocity, vec3d& rotational_velocity)
163167
{
164168
// calc what the timing was previously.
165-
float numerator = static_cast<float>(_packets[next_packet_index].remote_missiontime) - static_cast<float>(stamp.value());
169+
float numerator = static_cast<float>(stamp.value()) - static_cast<float>(_packets[prev_packet_index].remote_missiontime);
166170
float denominator = static_cast<float>(_packets[next_packet_index].remote_missiontime) - static_cast<float>(_packets[prev_packet_index].remote_missiontime);
167171

168172
denominator = (denominator > 0.05f) ? denominator : 0.05f;
@@ -174,7 +178,7 @@ void interpolation_manager::reinterpolate_previous(TIMESTAMP stamp, int prev_pac
174178

175179
physics_snapshot temp_snapshot;
176180

177-
physics_interpolate_snapshots(temp_snapshot, _packets[_prev_packet_index].snapshot, _packets[_upcoming_packet_index].snapshot, scale);
181+
physics_interpolate_snapshots(temp_snapshot, _packets[prev_packet_index].snapshot, _packets[next_packet_index].snapshot, scale);
178182
physics_apply_snapshot_manual(position, orientation, velocity, rotational_velocity, temp_snapshot);
179183
}
180184

@@ -213,21 +217,26 @@ void interpolation_manager::add_packet(int objnum, int frame, int packet_timesta
213217
_prev_packet_index = 1;
214218
}
215219

216-
if (static_cast<int>(_packets.size()) > PACKET_INFO_LIMIT) {
220+
if (_packets.size() > PACKET_INFO_LIMIT) {
217221
_packets.pop_back();
218222
}
219223

220-
// whenenver the server gets a player packet, we need to update the ship record, since the old info is now stale
221-
if (Objects[objnum].flags[Object::Object_Flags::Player_ship]){
224+
if ((_upcoming_packet_index >= 0) && (_prev_packet_index >= 0)) {
225+
// update packet indexes (ignoring simulation mode)
226+
// NOTE: indexes must be valid before being reassesed!
227+
reassess_packet_index();
222228

223-
int start_time = Multi_Timing_Info.get_mission_start_time();
229+
// whenenver the server gets a player packet, we need to update the ship record, since the old info is now stale
230+
if (Objects[objnum].flags[Object::Object_Flags::Player_ship]){
231+
int start_time = Multi_Timing_Info.get_mission_start_time();
224232

225-
multi_ship_record_signal_update(objnum, TIMESTAMP(start_time + _packets[_prev_packet_index].remote_missiontime), TIMESTAMP(start_time + _packets[_upcoming_packet_index].remote_missiontime), _prev_packet_index, _upcoming_packet_index);
233+
multi_ship_record_signal_update(objnum, TIMESTAMP(start_time + _packets[_prev_packet_index].remote_missiontime), TIMESTAMP(start_time + _packets[_upcoming_packet_index].remote_missiontime), _prev_packet_index, _upcoming_packet_index);
226234

227-
// if it's not the front packet, we need to update more info past the current packet, as well.
228-
// Should be rare though as it is a contingency for out of order packets.
229-
if (_upcoming_packet_index != 0){
230-
multi_ship_record_signal_update(objnum, TIMESTAMP(start_time + _packets[_upcoming_packet_index].remote_missiontime), TIMESTAMP(start_time + _packets[_upcoming_packet_index - 1].remote_missiontime), _upcoming_packet_index, _upcoming_packet_index - 1);
235+
// if it's not the front packet, we need to update more info past the current packet, as well.
236+
// Should be rare though as it is a contingency for out of order packets.
237+
if (_upcoming_packet_index > 0) {
238+
multi_ship_record_signal_update(objnum, TIMESTAMP(start_time + _packets[_upcoming_packet_index].remote_missiontime), TIMESTAMP(start_time + _packets[_upcoming_packet_index - 1].remote_missiontime), _upcoming_packet_index, _upcoming_packet_index - 1);
239+
}
231240
}
232241
}
233242

@@ -240,6 +249,11 @@ void interpolation_manager::add_packet(int objnum, int frame, int packet_timesta
240249
// should never replace index 0
241250
void interpolation_manager::replace_packet(int index, vec3d* pos, matrix* orient, physics_info* pip)
242251
{
252+
if (index < 1) {
253+
UNREACHABLE("Invalid replace interpolation packet index! (%d)", index);
254+
return;
255+
}
256+
243257
// the hackiest part of the hack? Setting its frame. Let FSO think that it was basically brand new.
244258
// it needs to handle it this way because otherwise another packet might get placed in front of it,
245259
// and we lose our intended effect of interpolating the simulation error away.

code/network/multi_interpolate.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
struct physics_info;
88

9-
constexpr int PACKET_INFO_LIMIT = 4; // we should never need more than 4 packets to do interpolation. Overwrite the oldest ones if we do.
9+
constexpr size_t PACKET_INFO_LIMIT = 4; // we should never need more than 4 packets to do interpolation. Overwrite the oldest ones if we do.
1010

1111
typedef struct packet_info {
1212

@@ -42,7 +42,7 @@ class interpolation_manager {
4242
SCP_vector<packet_info> _packets; // all the info from the position/orientation portion of packets that we care to keep
4343
int _source_player_index;
4444

45-
void reassess_packet_index(vec3d* pos, matrix* ori, physics_info* pip); // for finding which packets from within _packets we should use
45+
void reassess_packet_index(vec3d* pos = nullptr, matrix* ori = nullptr, physics_info* pip = nullptr); // for finding which packets from within _packets we should use
4646
void replace_packet(int index, vec3d* pos, matrix* orient, physics_info* pip); // a function that acts as a workaround, when coming out of simulation_mode
4747

4848
// Frame numbers that helps us figure out if we should ignore new information coming from the server because
@@ -52,6 +52,7 @@ class interpolation_manager {
5252
int _client_info_comparison_frame; // what frame was the last cleint info received?
5353
SCP_vector<std::pair<int,int>> _subsystems_comparison_frame; // what frame was the last subsystem information received? (for each subsystem) First is health, second is animation
5454
int _ai_comparison_frame; // what frame was the last ai information received?
55+
int _support_comparison_frame; // what frame was the last support information received?
5556

5657
public:
5758

@@ -85,6 +86,7 @@ class interpolation_manager {
8586

8687

8788
int get_ai_comparison_frame() const { return _ai_comparison_frame; }
89+
int get_support_comparison_frame() const { return _support_comparison_frame; }
8890

8991
void set_hull_comparison_frame(int frame) { _hull_comparison_frame = frame; }
9092
void set_shields_comparison_frame(int frame) { _shields_comparison_frame = frame; }
@@ -105,6 +107,7 @@ class interpolation_manager {
105107
}
106108

107109
void set_ai_comparison_frame(int frame) { _ai_comparison_frame = frame; }
110+
void set_support_comparison_frame(int frame) { _support_comparison_frame = frame; }
108111

109112
void force_interpolation_mode() { _simulation_mode = true; }
110113

@@ -134,6 +137,7 @@ class interpolation_manager {
134137
}
135138

136139
_ai_comparison_frame = -1;
140+
_support_comparison_frame = -1;
137141
}
138142

139143
void clean_up()
@@ -155,6 +159,7 @@ class interpolation_manager {
155159
_shields_comparison_frame = -1;
156160
_client_info_comparison_frame = -1;
157161
_ai_comparison_frame = -1;
162+
_support_comparison_frame = -1;
158163
_source_player_index = -1;
159164
}
160165
};

code/network/multi_obj.cpp

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,12 @@ struct oo_general_info {
139139

140140
oo_general_info Oo_info;
141141

142-
// flags
143-
bool Afterburn_hack = false; // HACK!!!
142+
// This is part of a fix for AB trails and related model animations not working
143+
// for other ships in multi. The "hack" part is so the fix applies to hosts as well.
144+
// This could possibly be revisited in a future multi bump with a better solution - taylor
145+
// See: https://github.com/scp-fs2open/fs2open.github.com/commit/186bd01
146+
// Or: "mantis bug 895 response" in SCP Internal on HLP forums
147+
static bool Afterburn_hack = false; // HACK!!!
144148

145149
// returns the last frame's index.
146150
int multi_find_prev_frame_idx();
@@ -632,10 +636,9 @@ void multi_ship_record_do_rollback()
632636

633637
// set up all restore points and ship portion of the collision list
634638
for (ship& cur_ship : Ships) {
635-
636-
// once this happens, we've run out of ships.
639+
// skip destroyed ships
637640
if (cur_ship.objnum < 0) {
638-
break;
641+
continue;
639642
}
640643

641644
objp = &Objects[cur_ship.objnum];
@@ -904,10 +907,10 @@ void multi_ship_record_signal_update(int objnum, TIMESTAMP lower_time_limit, TIM
904907
}
905908
}
906909

907-
if (prev_index < 0 || post_index < 0) {
908-
mprintf(("Getting prev_index %d and post_index %d, which is not valid, while trying to update the ship record.\n", prev_index, post_index));
910+
if ((prev_index < 0) || (prev_index == post_index)) {
909911
return;
910-
} else if (prev_index == post_index) {
912+
} else if (post_index < 0) {
913+
mprintf(("Getting prev_index %d and post_index %d, which is not valid, while trying to update the ship record.\n", prev_index, post_index));
911914
return;
912915
}
913916

@@ -1840,10 +1843,18 @@ int multi_oo_unpack_data(net_player* pl, ubyte* data, int seq_num, int time_delt
18401843
// SPECIAL CLIENT INFO
18411844
// ---------------------------------------------------------------------------------------------------------------
18421845

1846+
// make sure the ab hack is reset before we read in new info
1847+
Afterburn_hack = false;
1848+
18431849
// if this is from a player, read his button info
18441850
if(MULTIPLAYER_MASTER){
18451851
int r0 = multi_oo_unpack_client_data(pl, data + offset, seq_num > Interp_info[objnum].get_client_info_comparison_frame());
18461852
offset += r0;
1853+
1854+
// update comparison frame
1855+
if (seq_num > Interp_info[objnum].get_client_info_comparison_frame()) {
1856+
Interp_info[objnum].set_client_info_comparison_frame(seq_num);
1857+
}
18471858
}
18481859

18491860
// ---------------------------------------------------------------------------------------------------------------
@@ -2129,15 +2140,6 @@ int multi_oo_unpack_data(net_player* pl, ubyte* data, int seq_num, int time_delt
21292140

21302141
if( seq_num > Interp_info[objnum].get_ai_comparison_frame() ){
21312142
if ( shipp->ai_index >= 0 ){
2132-
// make sure to undo the wrap if it occurred during compression for unset ai mode.
2133-
if (umode == 255) {
2134-
Ai_info[shipp->ai_index].mode = -1;
2135-
}
2136-
else {
2137-
Ai_info[shipp->ai_index].mode = umode;
2138-
}
2139-
Ai_info[shipp->ai_index].submode = submode;
2140-
21412143
// set this guy's target objnum, and other info
21422144
target_objp = multi_get_network_object( target_signature );
21432145

@@ -2184,29 +2186,30 @@ int multi_oo_unpack_data(net_player* pl, ubyte* data, int seq_num, int time_delt
21842186
GET_INT(ai_submode);
21852187
GET_USHORT(dock_sig);
21862188

2187-
// verify that it's a valid ship
2188-
if((shipp != nullptr) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
2189-
// bash ai info, this info does not get rebashed, because it is not as vital.
2190-
Ai_info[shipp->ai_index].ai_flags.from_u64(ai_flags);
2191-
Ai_info[shipp->ai_index].mode = ai_mode;
2192-
Ai_info[shipp->ai_index].submode = ai_submode;
2193-
2194-
object *objp = multi_get_network_object( dock_sig );
2195-
if(objp != nullptr){
2196-
Ai_info[shipp->ai_index].support_ship_objnum = OBJ_INDEX(objp);
2197-
if ((objp->instance > -1) && (objp->type == OBJ_SHIP)) {
2198-
Ai_info[shipp->ai_index].goals[0].target_name = Ships[objp->instance].ship_name;
2199-
Ai_info[shipp->ai_index].goals[0].target_signature = objp->signature;
2200-
} else {
2201-
Ai_info[shipp->ai_index].goals[0].target_name = nullptr;
2202-
Ai_info[shipp->ai_index].goals[0].target_signature = 0;
2189+
if (seq_num > Interp_info[objnum].get_support_comparison_frame()) {
2190+
// verify that it's a valid ship
2191+
if((shipp != nullptr) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
2192+
// bash ai info, this info does not get rebashed, because it is not as vital.
2193+
Ai_info[shipp->ai_index].ai_flags.from_u64(ai_flags);
2194+
Ai_info[shipp->ai_index].mode = ai_mode;
2195+
Ai_info[shipp->ai_index].submode = ai_submode;
2196+
2197+
object *objp = multi_get_network_object( dock_sig );
2198+
if(objp != nullptr){
2199+
Ai_info[shipp->ai_index].support_ship_objnum = OBJ_INDEX(objp);
2200+
if ((objp->instance > -1) && (objp->type == OBJ_SHIP)) {
2201+
Ai_info[shipp->ai_index].goals[0].target_name = Ships[objp->instance].ship_name;
2202+
Ai_info[shipp->ai_index].goals[0].target_signature = objp->signature;
2203+
} else {
2204+
Ai_info[shipp->ai_index].goals[0].target_name = nullptr;
2205+
Ai_info[shipp->ai_index].goals[0].target_signature = 0;
2206+
}
22032207
}
22042208
}
2205-
}
2206-
}
22072209

2208-
// make sure the ab hack is reset before we read in new info
2209-
Afterburn_hack = false;
2210+
Interp_info[objnum].set_support_comparison_frame(seq_num);
2211+
}
2212+
}
22102213

22112214
// afterburner info
22122215
if ( (oo_flags & OO_AFTERBURNER_NEW) || Afterburn_hack ) {

code/network/multi_options.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,19 @@ void multi_options_read_config()
318318
Multi_options_g.webapiPort = (ushort) result;
319319
}
320320
}
321+
} else
322+
// set framerate for standalone
323+
if ( SETTING("+fps_cap") ) {
324+
NEXT_TOKEN();
325+
if (tok != nullptr) {
326+
if ((atoi(tok) >= 15) && (atoi(tok) <= 120)) {
327+
Multi_options_g.std_framecap = atoi(tok);
328+
}
329+
}
330+
} else
331+
// disable rollback for primary/dumbfire weapons
332+
if ( SETTING("+rollback_disabled") ) {
333+
Multi_options_g.std_rollback = false;
321334
}
322335
}
323336

code/network/multi_options.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ typedef struct multi_global_options {
5050
char std_passwd[STD_PASSWD_LEN+1]; // standalone host password
5151
char std_pname[STD_NAME_LEN+1]; // permanent name for the standalone - if any
5252
int std_framecap; // standalone frame cap
53+
bool std_rollback; // use rollback for primary/dumbfire shots
5354

5455
ushort webapiPort;
5556
SCP_string webapiUsername;
@@ -79,6 +80,7 @@ typedef struct multi_global_options {
7980
memset(std_passwd, 0, STD_PASSWD_LEN+1);
8081
memset(std_pname, 0, STD_NAME_LEN+1);
8182
std_framecap = 30;
83+
std_rollback = true;
8284

8385
webapiPort = 8080;
8486
webapiUsername = "admin";

code/network/multimsgs.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7845,7 +7845,7 @@ void send_non_homing_fired_packet(ship* shipp, int banks_or_number_of_missiles_f
78457845

78467846
// We need the time elpased, so send the last frame we got from the server and how much time has happened since then.
78477847
int last_received_frame = multi_client_lookup_frame_idx();
7848-
auto time_elapsed = static_cast<ushort>(timestamp_since(multi_client_lookup_frame_timestamp()));
7848+
auto time_elapsed = static_cast<ushort>(Multi_Timing_Info.get_current_time() - multi_client_lookup_frame_timestamp());
78497849

78507850
ADD_INT(last_received_frame);
78517851
ADD_USHORT(time_elapsed);
@@ -7939,7 +7939,7 @@ void process_non_homing_fired_packet(ubyte* data, header* hinfo)
79397939

79407940
object* objp_ref = multi_get_network_object(target_ref);
79417941

7942-
if (objp_ref == nullptr || objp_ref->type != OBJ_SHIP) {
7942+
if ((Is_standalone && !Multi_options_g.std_rollback) || !objp_ref || (objp_ref->type != OBJ_SHIP)) {
79437943
// new way failed, use the old new way.
79447944

79457945
if (objp_ref != nullptr){

code/ship/ship.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15449,7 +15449,7 @@ int ship_type_name_lookup(const char *name)
1544915449
// subsysp is a pointer to the subsystem.
1545015450
int get_subsystem_pos(vec3d* pos, const object* objp, const ship_subsys* subsysp)
1545115451
{
15452-
if (subsysp == NULL) {
15452+
if ( !subsysp || !subsysp->system_info ) {
1545315453
*pos = objp->pos;
1545415454
return 0;
1545515455
}

code/ship/shiphit.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "mission/missionlog.h"
3232
#include "mod_table/mod_table.h"
3333
#include "network/multi.h"
34+
#include "network/multi_interpolate.h"
3435
#include "network/multi_pmsg.h"
3536
#include "network/multi_respawn.h"
3637
#include "network/multimsgs.h"
@@ -2151,6 +2152,11 @@ void ship_apply_whack(const vec3d *force, const vec3d *hit_pos, object *objp)
21512152
game_whack_apply( -test.xyz.x, -test.xyz.y );
21522153
}
21532154

2155+
if ((Game_mode & GM_MULTIPLAYER) && (objp->type == OBJ_SHIP)) {
2156+
// temporarily set this as an uninterpolated ship, to make the collision
2157+
// look more natural until the next update comes in.
2158+
Interp_info[OBJ_INDEX(objp)].force_interpolation_mode();
2159+
}
21542160

21552161
if (object_is_docked(objp))
21562162
{

0 commit comments

Comments
 (0)