mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 12:00:26 +02:00
Merge pull request #101 from xshxdex98/coop-bsp-sync
Co-op: every machine stays on the host's BSP (network version 16)
This commit is contained in:
7 files changed
+123
-25
No files matched your search
@@ -47,7 +47,12 @@ with ideas from VALORANT's netcode articles, keeping the 30 Hz tick:
|
||||
host's is once that stops, or if they drift apart while it moves;
|
||||
- which named objects exist, so scripted creates and deletes match.
|
||||
|
||||
Every machine follows the host's structure BSP. A loading zone into a
|
||||
Every machine follows the host's structure BSP: a switch is sent at once
|
||||
and reliably, and each client's input says which BSP it has loaded.
|
||||
Until a client has the host's, the host takes none of its players'
|
||||
movement and none of its loading zones, and after any switch no loading
|
||||
zone switches again until every machine has the new BSP (ten seconds at
|
||||
most). A loading zone into a
|
||||
BSP the team hasn't been in brings every player to whoever crossed it,
|
||||
as split screen does; one back into a BSP it has been in switches only
|
||||
with two thirds of the living players at it (in the trigger, or within
|
||||
@@ -153,7 +158,8 @@ actors on its clients and sends the flinches and deaths the host picked;
|
||||
version 13 drives up to 1056 of the host's AI units on its clients (co-op's
|
||||
extra enemies), where 12 drove 288; version 14 sends co-op's device positions
|
||||
and its units opening and closing; version 15 sends co-op's allegiances with
|
||||
its presentation, a message of another size.
|
||||
its presentation, a message of another size; version 16 has a client's input
|
||||
say which structure BSP it has loaded (co-op).
|
||||
|
||||
A client plays by its host's rules: in another's game (searching for it,
|
||||
in its lobby, or playing it) the developer console, the telnet console
|
||||
|
||||
@@ -945,6 +945,10 @@ static struct
|
||||
real power;
|
||||
} host_sent_devices[MAXIMUM_OBJECTS_PER_MAP];
|
||||
|
||||
/* host: the structure BSP each client player's machine last said it has,
|
||||
NONE before its first input */
|
||||
static short host_player_structure_bsps[MAXIMUM_TRACKED_PLAYERS];
|
||||
|
||||
/* host, each tick: the devices that moved or changed power (and, with the
|
||||
resent state, all that ever have) */
|
||||
static void host_send_device_states(
|
||||
@@ -1903,6 +1907,7 @@ void network_coop_new_game(
|
||||
csmemset(host_sent_transforms, 0, sizeof(host_sent_transforms));
|
||||
csmemset(host_sent_looks, 0, sizeof(host_sent_looks));
|
||||
csmemset(host_sent_devices, 0, sizeof(host_sent_devices));
|
||||
csmemset(host_player_structure_bsps, NONE, sizeof(host_player_structure_bsps));
|
||||
csmemset(&host_sent_screen_effect, 0, sizeof(host_sent_screen_effect));
|
||||
csmemset(&host_resend, 0, sizeof(host_resend));
|
||||
csmemset(&players_vitality, 0, sizeof(players_vitality));
|
||||
@@ -2582,6 +2587,21 @@ word network_coop_device_group_entry_size(
|
||||
return sizeof(struct distributed_coop_device_group);
|
||||
}
|
||||
|
||||
void network_coop_note_player_structure_bsp(
|
||||
short player_index,
|
||||
short structure_bsp_index)
|
||||
{
|
||||
if (player_index >= 0 && player_index < MAXIMUM_TRACKED_PLAYERS)
|
||||
host_player_structure_bsps[player_index] = structure_bsp_index;
|
||||
}
|
||||
|
||||
boolean network_coop_player_has_structure_bsp(
|
||||
long player_index)
|
||||
{
|
||||
return !network_coop_active() || player_get(player_index)->local_player_index != NONE ||
|
||||
host_player_structure_bsps[DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index)] == global_structure_bsp_index_get();
|
||||
}
|
||||
|
||||
word network_coop_device_state_entry_size(
|
||||
void)
|
||||
{
|
||||
|
||||
@@ -52,6 +52,13 @@ void network_coop_note_device_snap(short group_index);
|
||||
/* unit_scripting_commands.c: a script set a unit's maximum or current
|
||||
vitality; TRUE if it was a co-op player's, now set on every player's */
|
||||
boolean network_coop_set_players_vitality(long unit_index, boolean maximum, real body, real shield);
|
||||
/* host: whether the player's machine has the host's structure BSP loaded
|
||||
(always outside co-op, and for the host's own players). Until a client has,
|
||||
the host takes none of its players' movement, which falls there with no
|
||||
floor, and lets them trigger no BSP switch (players.c). */
|
||||
boolean network_coop_player_has_structure_bsp(long player_index);
|
||||
/* network_distributed.c: the structure BSP a client's input says it has */
|
||||
void network_coop_note_player_structure_bsp(short player_index, short structure_bsp_index);
|
||||
/* units.c: a vehicle left the vehicle carrying it (a Pelican's Warthog drop) */
|
||||
void network_coop_vehicle_dropped(long vehicle_index, long carrier_index);
|
||||
|
||||
|
||||
@@ -320,7 +320,9 @@ ticks before it */
|
||||
struct distributed_player_input
|
||||
{
|
||||
byte player_index;
|
||||
byte pad[3];
|
||||
/* co-op: the structure BSP the client has loaded */
|
||||
byte structure_bsp_index;
|
||||
byte pad[2];
|
||||
/* the client's tick */
|
||||
long tick;
|
||||
/* the latest host tick the client has had a message of (the host
|
||||
@@ -2083,9 +2085,14 @@ static void distributed_apply_predictions(
|
||||
{
|
||||
distributed_predictions[player_index].valid = FALSE;
|
||||
distributed_predictions[player_index].taken_host_time = NONE;
|
||||
/* (of the unit it has now: not one of a life before) */
|
||||
if (unit_index != NONE && unit_index == state->unit_index)
|
||||
/* (of the unit it has now: not one of a life before; and not
|
||||
from a co-op client still loading the host's BSP, whose player
|
||||
falls there with no floor under it) */
|
||||
if (unit_index != NONE && unit_index == state->unit_index &&
|
||||
network_coop_player_has_structure_bsp(unit_get(unit_index)->unit.player_index))
|
||||
{
|
||||
distributed_take_prediction(player_index, unit_index, &bound);
|
||||
}
|
||||
}
|
||||
/* (what the host's next tick starts from) */
|
||||
if (unit_index != NONE)
|
||||
@@ -2257,6 +2264,7 @@ static void distributed_client_send_inputs(
|
||||
if (!update_client_distributed_input(local_player_index, &input->tick, &input->action, input->control_flags))
|
||||
continue;
|
||||
input->player_index = distributed_player_to_byte(player_index);
|
||||
input->structure_bsp_index = (byte)global_structure_bsp_index_get();
|
||||
input->host_time = distributed_host_time;
|
||||
count++;
|
||||
}
|
||||
@@ -2330,6 +2338,7 @@ static void distributed_handle_inputs(
|
||||
/* (a spectator's buttons pick whom it watches: not counted) */
|
||||
if (network_coop_active() && player->unit_index != NONE)
|
||||
distributed_note_input_actions(input->player_index, &action);
|
||||
network_coop_note_player_structure_bsp(input->player_index, input->structure_bsp_index);
|
||||
update_server_handle_distributed_input(DATUM_INDEX_NEW(input->player_index, player->identifier), input->tick,
|
||||
&action, input->control_flags, DISTRIBUTED_INPUT_HISTORY);
|
||||
if (input->host_time != NONE && (host_time == NONE || input->host_time > host_time))
|
||||
@@ -3001,10 +3010,13 @@ static void distributed_send_pings(
|
||||
}
|
||||
|
||||
/* Co-op: the host's structure BSP. Clients switch BSP only when told
|
||||
(their own trigger volumes don't, players.c). It is resent regularly so a
|
||||
client that lost a message or joined late catches up. */
|
||||
(their own trigger volumes don't, players.c). A switch is sent at once and
|
||||
reliably; it is also resent regularly so a client that joined late catches
|
||||
up. */
|
||||
#define STRUCTURE_BSP_INTERVAL_TICKS (TICKS_PER_SECOND / 2)
|
||||
|
||||
static short distributed_sent_structure_bsp_index = NONE;
|
||||
|
||||
struct distributed_structure_bsp
|
||||
{
|
||||
short structure_bsp_index;
|
||||
@@ -3021,10 +3033,14 @@ static void distributed_send_structure_bsp(
|
||||
void)
|
||||
{
|
||||
struct distributed_structure_bsp_message message;
|
||||
short index = global_structure_bsp_index_get();
|
||||
boolean switched = index != distributed_sent_structure_bsp_index;
|
||||
|
||||
message.structure_bsp.structure_bsp_index = global_structure_bsp_index_get();
|
||||
message.structure_bsp.structure_bsp_index = index;
|
||||
message.structure_bsp.pad = 0;
|
||||
distributed_send(&message, _distributed_message_structure_bsp, 1, (word)sizeof(message), _distributed_to_clients);
|
||||
distributed_send(&message, _distributed_message_structure_bsp, 1, (word)sizeof(message),
|
||||
switched ? _distributed_to_clients_reliably : _distributed_to_clients);
|
||||
distributed_sent_structure_bsp_index = index;
|
||||
}
|
||||
|
||||
static void distributed_handle_structure_bsp(
|
||||
@@ -3287,10 +3303,15 @@ void network_distributed_tick(
|
||||
distributed_statistics_due = FALSE;
|
||||
if (game_time_get() % PING_INTERVAL_TICKS == 0)
|
||||
distributed_send_pings();
|
||||
/* (before the players, so clients load a new BSP before they hear
|
||||
where the host moved everyone into it) */
|
||||
if (network_coop_active() && (global_structure_bsp_index_get() != distributed_sent_structure_bsp_index ||
|
||||
game_time_get() % STRUCTURE_BSP_INTERVAL_TICKS == 0))
|
||||
{
|
||||
distributed_send_structure_bsp();
|
||||
}
|
||||
distributed_host_send_players();
|
||||
network_actors_host_tick();
|
||||
if (network_coop_active() && game_time_get() % STRUCTURE_BSP_INTERVAL_TICKS == 0)
|
||||
distributed_send_structure_bsp();
|
||||
network_coop_host_tick();
|
||||
distributed_send_pickups();
|
||||
if (game_time_get() % GAME_STATE_INTERVAL_TICKS == 0)
|
||||
|
||||
@@ -1795,6 +1795,10 @@ void network_objects_apply_vehicle_predictions(
|
||||
{
|
||||
continue;
|
||||
}
|
||||
/* (not from a co-op client still loading the host's BSP: the
|
||||
vehicle falls there with no ground under it) */
|
||||
if (!network_coop_player_has_structure_bsp(driver->unit.player_index))
|
||||
continue;
|
||||
dx = state->position.x - vehicle->object.position.x;
|
||||
dy = state->position.y - vehicle->object.position.y;
|
||||
dz = state->position.z - vehicle->object.position.z;
|
||||
|
||||
@@ -66,7 +66,7 @@ is newer (network_client_manager.c). A host advertises it, with its netcode,
|
||||
in its game's advertisement's reserved bytes (network_server_message_handler.c),
|
||||
which hosts built before there was a version send as zeros: version 0.
|
||||
Raise it with any change to what the machines send each other. */
|
||||
#define HALO_PORT_NETWORK_VERSION 15
|
||||
#define HALO_PORT_NETWORK_VERSION 16
|
||||
/* ... the advertisement's reserved bytes: the version (a little-endian word),
|
||||
then flags */
|
||||
#define HALO_PORT_ADVERTISED_VERSION_OFFSET 0
|
||||
|
||||
+53
-13
@@ -470,9 +470,10 @@ static struct
|
||||
long respawn_wait_since;
|
||||
/* when each player's unit left the loaded structure BSP */
|
||||
long stranded_since[NETWORK_GAME_MAXIMUM_PLAYER_COUNT];
|
||||
/* no BSP switch trigger counts before this game time (one has just
|
||||
switched, and the players are being brought into the new BSP) */
|
||||
long bsp_switch_allowed_time;
|
||||
/* the structure BSP loaded, and when it last switched, by a trigger,
|
||||
a script or a skipped cutscene (players_coop_bsp_switch_ready) */
|
||||
short structure_bsp_index;
|
||||
long bsp_switch_time;
|
||||
/* the structure BSPs the players have been in this map, by bit */
|
||||
word visited_structure_bsps;
|
||||
/* where each player last stood on the ground, and on which BSP: where
|
||||
@@ -2771,8 +2772,10 @@ someone who is only crossing a seam or riding over a gap. */
|
||||
#define COOP_STRANDED_TICKS (2 * TICKS_PER_SECOND)
|
||||
|
||||
/* After a BSP switch, how long the co-op host lets no other trigger switch
|
||||
it while the players are brought into the new one */
|
||||
it while the players are brought into the new one, and the longest it then
|
||||
waits for every machine to have loaded it */
|
||||
#define COOP_BSP_SWITCH_SETTLE_TICKS (2 * TICKS_PER_SECOND)
|
||||
#define COOP_BSP_SWITCH_WAIT_TICKS (10 * TICKS_PER_SECOND)
|
||||
|
||||
/* Going back to a BSP the team has already been in needs two thirds of the
|
||||
living players at the trigger: inside it, or this near the player in it
|
||||
@@ -2987,10 +2990,37 @@ static boolean players_coop_in_bsp_switch_trigger(
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* port: whether a network co-op host lets any trigger switch the BSP yet.
|
||||
After a switch it waits COOP_BSP_SWITCH_SETTLE_TICKS while the players are
|
||||
brought in, then until every player's machine has loaded the new BSP, so no
|
||||
client falls two switches behind. A client stuck longer than
|
||||
COOP_BSP_SWITCH_WAIT_TICKS no longer holds the team. */
|
||||
static boolean players_coop_bsp_switch_ready(
|
||||
void)
|
||||
{
|
||||
long since = game_time_get() - players_coop_state.bsp_switch_time;
|
||||
struct data_iterator iterator;
|
||||
struct player_datum *player;
|
||||
|
||||
if (since < COOP_BSP_SWITCH_SETTLE_TICKS)
|
||||
return FALSE;
|
||||
if (since >= COOP_BSP_SWITCH_WAIT_TICKS)
|
||||
return TRUE;
|
||||
data_iterator_new(&iterator, player_data);
|
||||
while ((player = data_iterator_next(&iterator)) != NULL)
|
||||
{
|
||||
if (player->unit_index != NONE && !network_coop_player_has_structure_bsp(iterator.datum_index))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* port: whether a network co-op host lets the unit on this trigger switch
|
||||
the BSP, which brings the whole team along. Anyone can lead the team into a
|
||||
BSP it hasn't been in; going back to one needs two thirds of the team
|
||||
there, so one player can't drag everyone back through the level. */
|
||||
the BSP, which brings the whole team along. Not while the unit's machine
|
||||
is still loading the BSP the host has. Anyone can lead the team into a BSP
|
||||
it hasn't been in; going back to one needs two thirds of the team there,
|
||||
so one player can't drag everyone back through the level. */
|
||||
static boolean players_coop_bsp_switch_allowed(
|
||||
short bsp_switch_trigger_volume_index,
|
||||
long unit_index)
|
||||
@@ -3004,6 +3034,8 @@ static boolean players_coop_bsp_switch_allowed(
|
||||
short living_count = 0;
|
||||
short gathered_count = 0;
|
||||
|
||||
if (!network_coop_player_has_structure_bsp(unit_get(unit_index)->unit.player_index))
|
||||
return FALSE;
|
||||
if (!VALID_INDEX(volume->destination_structure_bsp_index, MAXIMUM_STRUCTURE_BSPS_PER_SCENARIO) ||
|
||||
!TEST_FLAG(players_coop_state.visited_structure_bsps, volume->destination_structure_bsp_index))
|
||||
{
|
||||
@@ -3053,7 +3085,7 @@ static void player_teleport_on_bsp_switch(
|
||||
whoever crossed it, as split screen does. Only one BSP is ever
|
||||
loaded, so anyone left elsewhere would stand outside it, or on a
|
||||
trigger that switches it straight back (players_update_after_game
|
||||
waits COOP_BSP_SWITCH_SETTLE_TICKS before another switch). */
|
||||
waits for players_coop_bsp_switch_ready before another switch). */
|
||||
if (network_coop_active())
|
||||
{
|
||||
outside_switch_trigger = TRUE;
|
||||
@@ -3093,6 +3125,10 @@ static void player_teleport_on_bsp_switch(
|
||||
player_index,
|
||||
source_unit_index,
|
||||
position);
|
||||
/* port: a big co-op team runs out of room around the
|
||||
crosser, so the rest go beside whoever is already in */
|
||||
if (!teleport_succeeded && network_coop_active())
|
||||
teleport_succeeded = player_place_beside_teammate(player_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4550,6 +4586,11 @@ void players_update_after_game(
|
||||
}
|
||||
if (VALID_INDEX(global_structure_bsp_index, MAXIMUM_STRUCTURE_BSPS_PER_SCENARIO))
|
||||
SET_FLAG(players_coop_state.visited_structure_bsps, global_structure_bsp_index, TRUE);
|
||||
if (players_coop_state.structure_bsp_index != global_structure_bsp_index)
|
||||
{
|
||||
players_coop_state.structure_bsp_index = global_structure_bsp_index;
|
||||
players_coop_state.bsp_switch_time = game_time_get();
|
||||
}
|
||||
|
||||
data_iterator_new(&iterator, player_data);
|
||||
while (player = data_iterator_next(&iterator))
|
||||
@@ -4597,11 +4638,10 @@ void players_update_after_game(
|
||||
root_object_index = object_get_ultimate_parent(player->unit_index);
|
||||
root_object = object_get(root_object_index);
|
||||
/* port: a co-op client only switches BSP when the host does
|
||||
(network_distributed.c), and the host not again until the players
|
||||
are in the new one */
|
||||
(network_distributed.c), and the host not again until everyone
|
||||
has the new one */
|
||||
if (!TEST_FLAG(root_object->object.flags, _object_outside_of_map_bit) &&
|
||||
!(network_coop_active() &&
|
||||
(network_game_distributed_client() || game_time_get() < players_coop_state.bsp_switch_allowed_time)))
|
||||
!(network_coop_active() && (network_game_distributed_client() || !players_coop_bsp_switch_ready())))
|
||||
{
|
||||
scenario = global_scenario_get();
|
||||
for (bsp_switch_trigger_volume_index = 0;
|
||||
@@ -4637,7 +4677,7 @@ void players_update_after_game(
|
||||
bsp_switch_trigger_volume_index;
|
||||
main_switch_structure_bsp(
|
||||
bsp_switch_trigger_volume->destination_structure_bsp_index);
|
||||
players_coop_state.bsp_switch_allowed_time = game_time_get() + COOP_BSP_SWITCH_SETTLE_TICKS;
|
||||
players_coop_state.bsp_switch_time = game_time_get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user