Compare commits

...
Author SHA1 Message Date
Stefan MaierandClaude Opus 5.5 28fdc704c6 VR multiplayer: the headset's part of the input travels with it
Halo plays in lockstep: every machine simulates every player from the
inputs the host hands out each tick. The VR port's simulation read the
headset directly (the weapon's aim and muzzle, the head's look, a seated
gun's eyes, the grenade held and thrown by hand, a swing's melee
strength), which only the headset's machine knows. These now make a VR
block beside each action (game/players.h, in whole numbers so every
machine, the host included, works with the same values; positions as
offsets from the unit's camera), sampled where the input is built
(player_control.c), carried by the queues beside the actions, and by the
game update messages as a list of the players who have one (system link
message version 3). The simulation reads the block, on every native
build: a desktop machine simulates a VR player as the headset does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:10:28 +02:00
15 changed files with 555 additions and 78 deletions

No files matched your search

+5 -3
View File
@@ -57,15 +57,17 @@ The native builds' messages differ from the Xbox game's (longer arrays, the
game settings record in fragments), so they search for games with their own game settings record in fragments), so they search for games with their own
protocol version and never see the Xbox game's, or it theirs. */ protocol version and never see the Xbox game's, or it theirs. */
#define HALO_PORT_NETWORK_GAME_MESSAGE_VERSION 2 /* 3: game updates carry the VR players' blocks (game/players.h) */
#define HALO_PORT_NETWORK_GAME_MESSAGE_VERSION 3
/* a message header's 12-bit length allows messages of up to 0xFFF bytes, /* a message header's 12-bit length allows messages of up to 0xFFF bytes,
header included; the per-tick update of 128 players is 3,857 */ header included; the per-tick update of 128 players is 3,857 */
#define HALO_PORT_MAXIMUM_NETWORK_MESSAGE_SIZE 0x1000 #define HALO_PORT_MAXIMUM_NETWORK_MESSAGE_SIZE 0x1000
/* the packet codec's limit on a decoded or encoded packet; the per-tick /* the packet codec's limit on a decoded or encoded packet; the per-tick
update of 128 players decodes to 0x1010 bytes */ update of 128 players decodes to 0x12D4 bytes, 16 VR players' blocks
#define HALO_PORT_NETWORK_PACKET_SIZE 0x1100 (game/players.h) included */
#define HALO_PORT_NETWORK_PACKET_SIZE 0x1400
/* the game settings record (HALO_PORT_NETWORK_GAME_SIZE, 13,092 bytes at 128 /* the game settings record (HALO_PORT_NETWORK_GAME_SIZE, 13,092 bytes at 128
machines and players) does not fit one message; it is sent in pieces of machines and players) does not fit one message; it is sent in pieces of
+8 -7
View File
@@ -587,22 +587,23 @@ long player_aim_projectile(
real autoaim_level; real autoaim_level;
director_camera_deterministic(player->unit_index, &camera_position, &camera_direction); director_camera_deterministic(player->unit_index, &camera_position, &camera_direction);
#ifdef HALO_ANDROID #ifdef HALO_LINUX
if (halo_vr_active() && player->local_player_index==0) if (player_action_vr_get(player_index))
{ {
struct player_action_vr const *vr_action= player_action_vr_get(player_index);
struct unit_datum *vr_unit= unit_get(player->unit_index); struct unit_datum *vr_unit= unit_get(player->unit_index);
float vr_eyes[3], vr_heading[3];
if (vr_unit->object.parent_object_index!=NONE && if (vr_unit->object.parent_object_index!=NONE &&
halo_vr_body_frame(player->local_player_index, vr_eyes, vr_heading)) TEST_FLAG(vr_action->flags, _player_action_vr_eyes_bit))
{ {
/* VR, seated: as the game aims a vehicle's gun, at what lies /* VR, seated: as the game aims a vehicle's gun, at what lies
along the player's aim, from the eyes (vr.c draws the crosshair along the player's aim, from the eyes (vr.c draws the crosshair
there); its barrel alone points elsewhere (a Banshee's nose there); its barrel alone points elsewhere (a Banshee's nose
rises as it hovers, a tank's turret lags) */ rises as it hovers, a tank's turret lags) */
camera_position.x= vr_eyes[0]; unit_get_camera_position(player->unit_index, &camera_position);
camera_position.y= vr_eyes[1]; camera_position.x+= player_action_vr_offset(vr_action->eyes[0]);
camera_position.z= vr_eyes[2]; camera_position.y+= player_action_vr_offset(vr_action->eyes[1]);
camera_position.z+= player_action_vr_offset(vr_action->eyes[2]);
camera_direction= vr_unit->unit.aiming_vector; camera_direction= vr_unit->unit.aiming_vector;
} }
else else
+89
View File
@@ -389,6 +389,81 @@ static void player_control_vr_spawn_vehicle(
platform_log("VR: spawning %s: %s", name, object_new(&data) == NONE ? "failed" : "done"); platform_log("VR: spawning %s: %s", name, object_new(&data) == NONE ? "failed" : "done");
} }
#endif
#ifdef HALO_ANDROID
/* VR: what the headset adds to the local player's input this tick (game/
players.h), in the whole numbers every machine in a game works with; the
simulation reads it from the input, never from the headset */
static void player_control_vr_quantize_direction(
const float direction[3],
short quantized[3])
{
short axis;
for (axis = 0; axis < 3; axis++)
quantized[axis] = player_action_vr_quantize_direction(direction[axis]);
}
static void player_control_vr_quantize_offset(
const float point[3],
real_point3d const *camera,
short quantized[3])
{
quantized[0] = player_action_vr_quantize_offset(point[0] - camera->x);
quantized[1] = player_action_vr_quantize_offset(point[1] - camera->y);
quantized[2] = player_action_vr_quantize_offset(point[2] - camera->z);
}
static void player_control_vr_sample(
short local_player_index,
struct player_datum *player,
struct player_action_vr *vr)
{
float vector[3], point[3], strength;
real_point3d camera;
boolean has_unit = player->unit_index != NONE;
if (!halo_vr_active() || local_player_index != 0)
return;
if (has_unit)
unit_get_camera_position(player->unit_index, &camera);
if (halo_vr_aim_vector(local_player_index, vector))
{
vr->flags |= FLAG(_player_action_vr_aim_bit);
player_control_vr_quantize_direction(vector, vr->aim);
}
if (halo_vr_look_vector(local_player_index, vector))
{
vr->flags |= FLAG(_player_action_vr_look_bit);
player_control_vr_quantize_direction(vector, vr->look);
}
if (has_unit && halo_vr_weapon_origin(local_player_index, point))
{
vr->flags |= FLAG(_player_action_vr_muzzle_bit);
player_control_vr_quantize_offset(point, &camera, vr->muzzle);
}
if (has_unit && halo_vr_body_frame(local_player_index, point, vector))
{
vr->flags |= FLAG(_player_action_vr_eyes_bit);
player_control_vr_quantize_offset(point, &camera, vr->eyes);
}
if (halo_vr_grenade_held(local_player_index))
vr->flags |= FLAG(_player_action_vr_grenade_held_bit);
if (has_unit && halo_vr_grenade_throw(local_player_index, point, vector, &strength))
{
vr->flags |= FLAG(_player_action_vr_grenade_thrown_bit);
player_control_vr_quantize_offset(point, &camera, vr->grenade_offset);
player_control_vr_quantize_direction(vector, vr->grenade_direction);
vr->grenade_strength = player_action_vr_quantize_direction(strength);
}
strength = halo_vr_melee_strength();
if (strength > 0.f)
{
vr->flags |= FLAG(_player_action_vr_melee_bit);
vr->melee_strength = player_action_vr_quantize_direction(strength);
}
}
#endif #endif
/* ---------- public code */ /* ---------- public code */
@@ -879,6 +954,20 @@ static void handle_one_player_input(
0x36A, 0x36A,
action.desired_facing.yaw); action.desired_facing.yaw);
update_client_queue(&action); update_client_queue(&action);
#ifdef HALO_LINUX
{
/* and what the headset adds to it (game/players.h), for every
local player so their slots stay in step */
struct player_action_vr vr;
csmemset(&vr, 0, sizeof(vr));
#ifdef HALO_ANDROID
player_control_vr_sample(local_player_index,
player_get(local_player_get_player_index(local_player_index)), &vr);
#endif
update_client_queue_vr(&vr);
}
#endif
} }
return; return;
+184
View File
@@ -257,6 +257,16 @@ static struct update *update_client_get_update(
static struct update_server_globals update_server_globals = { 0 }; static struct update_server_globals update_server_globals = { 0 };
static struct update_client_globals update_client_globals = { 0 }; static struct update_client_globals update_client_globals = { 0 };
#ifdef HALO_LINUX
/* VR (players.h): each step's VR blocks, beside its actions. A grenade the
hand threw between two ticks stays thrown until a tick has run. */
static struct player_action_vr update_client_pending_vr[MAXIMUM_LOCAL_PLAYERS];
static struct player_action_vr update_client_saved_vr[MAXIMUM_LOCAL_PLAYERS];
static struct player_action_vr update_server_queue_vr[MAXIMUM_NUMBER_OF_PLAYERS];
static struct player_action_vr update_server_updates_vr[MAXIMUM_SERVER_UPDATES][MAXIMUM_NUMBER_OF_PLAYERS];
static struct player_action_vr update_client_updates_vr[MAXIMUM_CLIENT_UPDATES][MAXIMUM_NUMBER_OF_PLAYERS];
static struct player_action_vr update_client_current_vr[MAXIMUM_NUMBER_OF_PLAYERS];
#endif
/* ---------- public code */ /* ---------- public code */
@@ -376,6 +386,13 @@ void update_server_next_update(
update->update.action_count += 1; update->update.action_count += 1;
} }
update_client_handle_server_update(&update->update, update_number); update_client_handle_server_update(&update->update, update_number);
#ifdef HALO_LINUX
csmemcpy(
update_server_updates_vr[update - update_server_globals.updates],
update_server_queue_vr,
sizeof(update_server_queue_vr));
update_client_handle_server_vr(update_server_updates_vr[update - update_server_globals.updates], update_number);
#endif
return; return;
} }
@@ -516,6 +533,7 @@ static unsigned long update_client_pending_control_flags[MAXIMUM_LOCAL_PLAYERS];
static real update_client_pending_primary_triggers[MAXIMUM_LOCAL_PLAYERS]; static real update_client_pending_primary_triggers[MAXIMUM_LOCAL_PLAYERS];
static long update_client_pending_game_time = NONE; static long update_client_pending_game_time = NONE;
#endif #endif
void update_client_queue( void update_client_queue(
struct player_action const *action) struct player_action const *action)
@@ -543,6 +561,35 @@ void update_client_queue(
return; return;
} }
#ifdef HALO_LINUX
/* the VR block of the action just queued (update_client_queue) */
void update_client_queue_vr(
struct player_action_vr const *vr)
{
long local_player = update_client_globals.current_local_player - 1;
if (local_player >= 0 && local_player < MAXIMUM_LOCAL_PLAYERS)
{
struct player_action_vr *pending = &update_client_pending_vr[local_player];
struct player_action_vr thrown = *pending;
short held_thrown = pending->flags & FLAG(_player_action_vr_grenade_thrown_bit);
*pending = *vr;
if (held_thrown && !(vr->flags & FLAG(_player_action_vr_grenade_thrown_bit)))
{
/* thrown on an earlier frame of this tick */
pending->flags |= held_thrown;
csmemcpy(pending->grenade_offset, thrown.grenade_offset, sizeof(pending->grenade_offset));
csmemcpy(pending->grenade_direction, thrown.grenade_direction, sizeof(pending->grenade_direction));
pending->grenade_strength = thrown.grenade_strength;
}
update_client_saved_vr[local_player] = *pending;
}
return;
}
#endif
void update_client_queue_push( void update_client_queue_push(
void) void)
{ {
@@ -552,6 +599,7 @@ void update_client_queue_push(
game_time_get_paused()) game_time_get_paused())
{ {
update_client_pending_game_time = game_time_get(); update_client_pending_game_time = game_time_get();
csmemset(update_client_pending_vr, 0, sizeof(update_client_pending_vr));
csmemset( csmemset(
update_client_pending_control_flags, update_client_pending_control_flags,
0, 0,
@@ -567,6 +615,9 @@ void update_client_queue_push(
&update_client_globals.saved_action_collection, &update_client_globals.saved_action_collection,
0, 0,
sizeof(update_client_globals.saved_action_collection)); sizeof(update_client_globals.saved_action_collection));
#ifdef HALO_LINUX
csmemset(update_client_saved_vr, 0, sizeof(update_client_saved_vr));
#endif
return; return;
} }
@@ -644,6 +695,12 @@ boolean update_client_dequeue(
0x1E8, 0x1E8,
(NONE == actions[queue_index].desired_zoom_level) || (actions[queue_index].desired_zoom_level>=0)); (NONE == actions[queue_index].desired_zoom_level) || (actions[queue_index].desired_zoom_level>=0));
} }
#ifdef HALO_LINUX
csmemcpy(
update_client_current_vr,
update_client_updates_vr[update - update_client_globals.updates],
sizeof(update_client_current_vr));
#endif
update_client_globals.next_update_number_to_dequeue += 1; update_client_globals.next_update_number_to_dequeue += 1;
return TRUE; return TRUE;
@@ -691,6 +748,14 @@ void update_client_local_ticks(
game_connection()==_game_connection_local); game_connection()==_game_connection_local);
update_client_build_client_update(&action_collection); update_client_build_client_update(&action_collection);
update_server_handle_client_update(machine_index, action_collection.actions); update_server_handle_client_update(machine_index, action_collection.actions);
#ifdef HALO_LINUX
{
struct player_action_vr vr[MAXIMUM_LOCAL_PLAYERS];
update_client_build_client_vr(vr);
update_server_handle_client_vr(machine_index, vr);
}
#endif
while (ticks-->0) while (ticks-->0)
{ {
update_server_next_update(); update_server_next_update();
@@ -792,6 +857,118 @@ void update_client_handle_server_update(
return; return;
} }
#ifdef HALO_LINUX
void update_client_build_client_vr(
struct player_action_vr *vr)
{
csmemcpy(vr, update_client_saved_vr, sizeof(update_client_saved_vr));
return;
}
/* as update_server_handle_client_update: the machine's players, in order */
void update_server_handle_client_vr(
long machine_index,
struct player_action_vr const *vr)
{
long *player_list = machine_get_player_list(machine_index);
long action_index = 0;
long player_index;
for (player_index = 0; player_index<MAXIMUM_LOCAL_PLAYERS; ++player_index)
{
if (player_list[player_index]!=NONE)
{
long absolute_index = DATUM_INDEX_TO_ABSOLUTE_INDEX(player_list[player_index]);
if (absolute_index<MAXIMUM_NUMBER_OF_PLAYERS)
update_server_queue_vr[absolute_index] = vr[action_index];
action_index++;
}
}
return;
}
/* the VR blocks of an update the host built (update_server_build_server_update) */
struct player_action_vr const *update_server_get_update_vr(
long update_number)
{
struct update *update = update_server_get_update(update_number);
return update ? update_server_updates_vr[update - update_server_globals.updates] : NULL;
}
/* as update_client_handle_server_update, for its VR blocks */
void update_client_handle_server_vr(
struct player_action_vr const *vr,
long update_number)
{
struct update *client_update = update_client_get_update(update_number);
if (client_update)
{
csmemcpy(
update_client_updates_vr[client_update - update_client_globals.updates],
vr,
sizeof(update_client_updates_vr[0]));
}
return;
}
struct player_action_vr const *player_action_vr_get(
long player_index)
{
long absolute_index = DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index);
if (player_index==NONE || absolute_index<0 || absolute_index>=MAXIMUM_NUMBER_OF_PLAYERS ||
!update_client_current_vr[absolute_index].flags)
{
return NULL;
}
return &update_client_current_vr[absolute_index];
}
short player_action_vr_quantize_direction(
real value)
{
return (short)PIN((long)(value * 32767.0f + (value < 0.0f ? -0.5f : 0.5f)), -32767, 32767);
}
real player_action_vr_direction(
short value)
{
return value * (1.0f / 32767.0f);
}
short player_action_vr_quantize_offset(
real value)
{
return (short)PIN((long)(value * 4096.0f + (value < 0.0f ? -0.5f : 0.5f)), -32767, 32767);
}
real player_action_vr_offset(
short value)
{
return value * (1.0f / 4096.0f);
}
/* a direction, made whole again (every machine rounds the same) */
void player_action_vr_get_direction(
short const quantized[3],
real_vector3d *direction)
{
direction->i = player_action_vr_direction(quantized[0]);
direction->j = player_action_vr_direction(quantized[1]);
direction->k = player_action_vr_direction(quantized[2]);
if (normalize3d(direction) == 0.0f)
*direction = *global_forward3d;
return;
}
#endif
void update_queues_reset_and_fill_with_lies( void update_queues_reset_and_fill_with_lies(
void) void)
{ {
@@ -811,6 +988,13 @@ void update_queues_reset_and_fill_with_lies(
&update_client_globals.saved_action_collection, &update_client_globals.saved_action_collection,
0, 0,
sizeof(update_client_globals.saved_action_collection)); sizeof(update_client_globals.saved_action_collection));
#ifdef HALO_LINUX
csmemset(update_client_updates_vr, 0, sizeof(update_client_updates_vr));
csmemset(update_client_current_vr, 0, sizeof(update_client_current_vr));
csmemset(update_client_saved_vr, 0, sizeof(update_client_saved_vr));
csmemset(update_server_queue_vr, 0, sizeof(update_server_queue_vr));
csmemset(update_server_updates_vr, 0, sizeof(update_server_updates_vr));
#endif
update_client_globals.latest_update_number_received = NONE; update_client_globals.latest_update_number_received = NONE;
update_client_globals.next_update_number_to_dequeue = 0; update_client_globals.next_update_number_to_dequeue = 0;
game_time = game_time_get(); game_time = game_time_get();
+19
View File
@@ -58,6 +58,25 @@ void update_client_handle_server_update(
struct server_update *update, struct server_update *update,
long update_number); long update_number);
#ifdef HALO_LINUX
/* VR (players.h, struct player_action_vr): each step of an action's way has
its VR block's beside it */
struct player_action_vr;
void update_client_queue_vr(
struct player_action_vr const *vr);
void update_client_build_client_vr(
struct player_action_vr *vr);
void update_server_handle_client_vr(
long machine_index,
struct player_action_vr const *vr);
struct player_action_vr const *update_server_get_update_vr(
long update_number);
void update_client_handle_server_vr(
struct player_action_vr const *vr,
long update_number);
#endif
void update_queues_reset_and_fill_with_lies( void update_queues_reset_and_fill_with_lies(
void); void);
long player_new_queue( long player_new_queue(
+13 -20
View File
@@ -3486,31 +3486,24 @@ void players_update_before_game(
&action->desired_facing); &action->desired_facing);
control_data.facing_vector = control_data.facing_vector =
control_data.looking_vector = control_data.aiming_vector; control_data.looking_vector = control_data.aiming_vector;
#ifdef HALO_ANDROID #ifdef HALO_LINUX
if (unit->object.parent_object_index == NONE) if (unit->object.parent_object_index == NONE && player_action_vr_get(iterator.datum_index))
{ {
/* VR: on foot, the weapon aims where the right controller /* VR: on foot, the weapon aims where the right controller
points, while the body and the view keep facing where the points, while the body and the view keep facing where the
head looks (port/linux/src/vr.c) */ head looks (port/linux/src/vr.c; the player's input carries
real vr_aim[3]; both, game/players.h); and it looks where the head looks,
real vr_look[3]; whether or not the game lets the player turn now (scripts
test it: unit_can_see_point) */
struct player_action_vr const *vr_action = player_action_vr_get(iterator.datum_index);
/* and it looks where the head looks, whether or not the if (TEST_FLAG(vr_action->flags, _player_action_vr_look_bit))
game lets the player turn now (scripts test it: player_action_vr_get_direction(vr_action->look, &control_data.looking_vector);
unit_can_see_point) */ if (TEST_FLAG(vr_action->flags, _player_action_vr_aim_bit))
if (halo_vr_look_vector(player->local_player_index, vr_look)) player_action_vr_get_direction(vr_action->aim, &control_data.aiming_vector);
{
control_data.looking_vector.i = vr_look[0];
control_data.looking_vector.j = vr_look[1];
control_data.looking_vector.k = vr_look[2];
}
if (halo_vr_aim_vector(player->local_player_index, vr_aim))
{
control_data.aiming_vector.i = vr_aim[0];
control_data.aiming_vector.j = vr_aim[1];
control_data.aiming_vector.k = vr_aim[2];
}
} }
#endif
#ifdef HALO_ANDROID
{ {
real unit_look[3]; real unit_look[3];
+74
View File
@@ -62,6 +62,80 @@ struct player_action
typedef char player_action_size_assert[ typedef char player_action_size_assert[
sizeof(struct player_action) == 0x20 ? 1 : -1]; sizeof(struct player_action) == 0x20 ? 1 : -1];
#ifdef HALO_LINUX
/* VR (port/linux/src/vr.c): what a player's headset adds to his input each
tick, in whole numbers so that every machine in a game, the host too, works
with exactly the same (player_queues_new.c carries it beside the action, and
the network messages as a list of the players who have one). Directions are
unit vectors in 1/32767ths, offsets from the unit's camera in 1/4096 world
units, strengths 0..1 in 1/32767ths. */
enum
{
_player_action_vr_aim_bit = 0, /* the weapon aims along aim */
_player_action_vr_look_bit, /* the head looks along look */
_player_action_vr_muzzle_bit, /* shots start at muzzle */
_player_action_vr_eyes_bit, /* seated, a gun aims from the eyes */
_player_action_vr_grenade_held_bit, /* the left trigger holds the grenade */
_player_action_vr_grenade_thrown_bit, /* the hand threw it this tick */
_player_action_vr_melee_bit, /* a swing's melee strength */
};
struct player_action_vr
{
short flags;
short aim[3];
short look[3];
short muzzle[3];
short eyes[3];
short grenade_offset[3];
short grenade_direction[3];
short grenade_strength;
short melee_strength;
};
typedef char player_action_vr_size_assert[
sizeof(struct player_action_vr) == 42 ? 1 : -1];
/* on the wire, after a game update's actions: the players who have a VR
block (their index in the update, or the machine's local player), up to
this many in a host's update */
enum
{
MAXIMUM_VR_PLAYERS_PER_UPDATE = 16,
};
struct player_action_vr_entry
{
short player;
struct player_action_vr vr;
};
typedef char player_action_vr_entry_size_assert[
sizeof(struct player_action_vr_entry) == 44 ? 1 : -1];
#define PLAYER_ACTION_VR_TAIL(maximum) \
short vr_count; \
struct player_action_vr_entry vr_entries[maximum]; \
short vr_pad;
/* the VR block of a player's input this tick (players_update_before_game),
or NULL if his input has none */
struct player_action_vr const *player_action_vr_get(
long player_index);
/* the conversions both ways */
short player_action_vr_quantize_direction(
real value);
real player_action_vr_direction(
short value);
short player_action_vr_quantize_offset(
real value);
real player_action_vr_offset(
short value);
void player_action_vr_get_direction(
short const quantized[3],
real_vector3d *direction);
#endif
typedef char player_action_desired_facing_yaw_offset_assert[ typedef char player_action_desired_facing_yaw_offset_assert[
offsetof(struct player_action, desired_facing.yaw) == 0x4 ? 1 : -1]; offsetof(struct player_action, desired_facing.yaw) == 0x4 ? 1 : -1];
typedef char player_action_desired_facing_pitch_offset_assert[ typedef char player_action_desired_facing_pitch_offset_assert[
+11 -9
View File
@@ -2254,19 +2254,21 @@ static void trigger_create_projectiles(
&up, &up,
trigger_definition->first_person_weapon_offset.z, trigger_definition->first_person_weapon_offset.z,
&origin); &origin);
#ifdef HALO_ANDROID #ifdef HALO_LINUX
if (unit->object.parent_object_index == NONE && unit->unit.gunner_object_index == NONE) if (unit->object.parent_object_index == NONE && unit->unit.gunner_object_index == NONE)
{ {
/* VR: from the muzzle of the weapon in the right hand; the /* VR: from the muzzle of the weapon in the right hand (the
projectile is kept inside the level from the camera below, player's input carries it, game/players.h); the projectile is
so a hand through a wall does not fire through it */ kept inside the level from the camera below, so a hand
real vr_origin[3]; through a wall does not fire through it */
struct player_action_vr const *vr_action = player_action_vr_get(player_index);
if (halo_vr_weapon_origin(player_get(player_index)->local_player_index, vr_origin)) if (vr_action && TEST_FLAG(vr_action->flags, _player_action_vr_muzzle_bit))
{ {
origin.x = vr_origin[0]; unit_get_camera_position(owner_object_index, &origin);
origin.y = vr_origin[1]; origin.x += player_action_vr_offset(vr_action->muzzle[0]);
origin.z = vr_origin[2]; origin.y += player_action_vr_offset(vr_action->muzzle[1]);
origin.z += player_action_vr_offset(vr_action->muzzle[2]);
} }
} }
#endif #endif
+23 -1
View File
@@ -619,6 +619,9 @@ struct message_server_game_update
byte __unknown0C[2]; byte __unknown0C[2];
short local_player_count; short local_player_count;
struct player_action player_actions[MAXIMUM_NUMBER_OF_PLAYERS]; struct player_action player_actions[MAXIMUM_NUMBER_OF_PLAYERS];
#ifdef HALO_LINUX
PLAYER_ACTION_VR_TAIL(MAXIMUM_VR_PLAYERS_PER_UPDATE)
#endif
}; };
struct server_update struct server_update
@@ -720,8 +723,10 @@ typedef char player_profile_size_assert[
#ifdef HALO_LINUX #ifdef HALO_LINUX
/* the update and settings layouts follow the session limits /* the update and settings layouts follow the session limits
(port/linux/include/halo_port_limits.h) */ (port/linux/include/halo_port_limits.h) */
/* (and the VR players' blocks, game/players.h) */
typedef char message_server_game_update_size_assert[ typedef char message_server_game_update_size_assert[
sizeof(struct message_server_game_update) == 0x10 + MAXIMUM_NUMBER_OF_PLAYERS * 0x20 ? 1 : -1]; sizeof(struct message_server_game_update) ==
0x10 + MAXIMUM_NUMBER_OF_PLAYERS * 0x20 + 2 + MAXIMUM_VR_PLAYERS_PER_UPDATE * 44 + 2 ? 1 : -1];
typedef char server_update_size_assert[ typedef char server_update_size_assert[
sizeof(struct server_update) == 4 + MAXIMUM_NUMBER_OF_PLAYERS * 0x20 ? 1 : -1]; sizeof(struct server_update) == 4 + MAXIMUM_NUMBER_OF_PLAYERS * 0x20 ? 1 : -1];
#else #else
@@ -1768,6 +1773,23 @@ boolean network_game_client_handle_game_update(
update.local_player_count * sizeof(struct player_action)); update.local_player_count * sizeof(struct player_action));
update_client_handle_server_update(&update, message_packet->update_number); update_client_handle_server_update(&update, message_packet->update_number);
#ifdef HALO_LINUX
{
/* the VR players' blocks (game/players.h) */
static struct player_action_vr vr[MAXIMUM_NUMBER_OF_PLAYERS];
short entry;
csmemset(vr, 0, sizeof(vr));
for (entry = 0; entry < message_packet->vr_count && entry < MAXIMUM_VR_PLAYERS_PER_UPDATE; entry++)
{
short player = message_packet->vr_entries[entry].player;
if (player >= 0 && player < MAXIMUM_NUMBER_OF_PLAYERS)
vr[player] = message_packet->vr_entries[entry].vr;
}
update_client_handle_server_vr(vr, message_packet->update_number);
}
#endif
client->next_update_number++; client->next_update_number++;
client->last_update_time = system_milliseconds(); client->last_update_time = system_milliseconds();
@@ -392,6 +392,9 @@ struct message_server_game_update
byte padding0C[2]; byte padding0C[2];
short local_player_count; short local_player_count;
struct player_action player_actions[MAXIMUM_NUMBER_OF_PLAYERS]; struct player_action player_actions[MAXIMUM_NUMBER_OF_PLAYERS];
#ifdef HALO_LINUX
PLAYER_ACTION_VR_TAIL(MAXIMUM_VR_PLAYERS_PER_UPDATE)
#endif
}; };
struct message_server_remove_player_ingame struct message_server_remove_player_ingame
+23
View File
@@ -175,6 +175,9 @@ struct client_game_update_message
short __unknown4; short __unknown4;
short local_player_count; short local_player_count;
byte update[0x80]; byte update[0x80];
#ifdef HALO_LINUX
PLAYER_ACTION_VR_TAIL(MAXIMUM_LOCAL_PLAYERS)
#endif
}; };
struct network_machine struct network_machine
@@ -606,6 +609,26 @@ boolean network_game_client_end_frame(
csmemcpy(message.update, &update, sizeof(update)); csmemcpy(message.update, &update, sizeof(update));
message.local_player_count = local_player_count(); message.local_player_count = local_player_count();
#ifdef HALO_LINUX
{
/* the local players' VR blocks (game/players.h) */
struct player_action_vr vr[MAXIMUM_LOCAL_PLAYERS];
short slot;
update_client_build_client_vr(vr);
message.vr_count = 0;
message.vr_pad = 0;
for (slot = 0; slot < MAXIMUM_LOCAL_PLAYERS; slot++)
{
if (vr[slot].flags)
{
message.vr_entries[message.vr_count].player = slot;
message.vr_entries[message.vr_count].vr = vr[slot];
message.vr_count++;
}
}
}
#endif
encoded_message = create_network_game_message( encoded_message = create_network_game_message(
_message_client_game_update, _message_client_game_update,
&message, &message,
+27 -2
View File
@@ -239,7 +239,12 @@ struct network_game_message_packet_definitions
struct data_packet_definition client_graceful_game_exit_pregame; struct data_packet_definition client_graceful_game_exit_pregame;
struct data_packet_field client_map_is_precached_pregame_fields[2]; struct data_packet_field client_map_is_precached_pregame_fields[2];
struct data_packet_definition client_map_is_precached_pregame; struct data_packet_definition client_map_is_precached_pregame;
#ifdef HALO_LINUX
/* ...and the VR players' blocks (game/players.h) */
struct data_packet_field server_game_update_fields[12];
#else
struct data_packet_field server_game_update_fields[8]; struct data_packet_field server_game_update_fields[8];
#endif
struct data_packet_definition server_game_update; struct data_packet_definition server_game_update;
struct data_packet_field server_add_player_ingame_fields[4]; struct data_packet_field server_add_player_ingame_fields[4];
struct data_packet_definition server_add_player_ingame; struct data_packet_definition server_add_player_ingame;
@@ -249,7 +254,11 @@ struct network_game_message_packet_definitions
struct data_packet_definition server_game_over; struct data_packet_definition server_game_over;
struct data_packet_field client_loaded_fields[2]; struct data_packet_field client_loaded_fields[2];
struct data_packet_definition client_loaded; struct data_packet_definition client_loaded;
#ifdef HALO_LINUX
struct data_packet_field client_game_update_fields[12];
#else
struct data_packet_field client_game_update_fields[8]; struct data_packet_field client_game_update_fields[8];
#endif
struct data_packet_definition client_game_update; struct data_packet_definition client_game_update;
struct data_packet_field client_add_player_request_ingame_fields[4]; struct data_packet_field client_add_player_request_ingame_fields[4];
struct data_packet_definition client_add_player_request_ingame; struct data_packet_definition client_add_player_request_ingame;
@@ -308,8 +317,8 @@ DEFINE_NETWORK_GAME_MESSAGE(message_client_game_start_request, 0x02);
DEFINE_NETWORK_GAME_MESSAGE(message_client_graceful_game_exit_pregame, 0x04); DEFINE_NETWORK_GAME_MESSAGE(message_client_graceful_game_exit_pregame, 0x04);
DEFINE_NETWORK_GAME_MESSAGE(message_client_map_is_precached_pregame, 0x100); DEFINE_NETWORK_GAME_MESSAGE(message_client_map_is_precached_pregame, 0x100);
#ifdef HALO_LINUX #ifdef HALO_LINUX
/* one 0x20-byte action per player */ /* one 0x20-byte action per player, and the VR players' 44-byte blocks */
DEFINE_NETWORK_GAME_MESSAGE(message_server_game_update, 0x10 + HALO_PORT_MAXIMUM_NETWORK_PLAYERS * 0x20); DEFINE_NETWORK_GAME_MESSAGE(message_server_game_update, 0x10 + HALO_PORT_MAXIMUM_NETWORK_PLAYERS * 0x20 + 2 + 16 * 44 + 2);
#else #else
DEFINE_NETWORK_GAME_MESSAGE(message_server_game_update, 0x210); DEFINE_NETWORK_GAME_MESSAGE(message_server_game_update, 0x210);
#endif #endif
@@ -317,7 +326,11 @@ DEFINE_NETWORK_GAME_MESSAGE(message_server_add_player_ingame, 0x20);
DEFINE_NETWORK_GAME_MESSAGE(message_server_remove_player_ingame, 0x24); DEFINE_NETWORK_GAME_MESSAGE(message_server_remove_player_ingame, 0x24);
DEFINE_NETWORK_GAME_MESSAGE(message_server_game_over, 0x04); DEFINE_NETWORK_GAME_MESSAGE(message_server_game_over, 0x04);
DEFINE_NETWORK_GAME_MESSAGE(message_client_loaded, 0x04); DEFINE_NETWORK_GAME_MESSAGE(message_client_loaded, 0x04);
#ifdef HALO_LINUX
DEFINE_NETWORK_GAME_MESSAGE(message_client_game_update, 0x88 + 2 + 4 * 44 + 2);
#else
DEFINE_NETWORK_GAME_MESSAGE(message_client_game_update, 0x88); DEFINE_NETWORK_GAME_MESSAGE(message_client_game_update, 0x88);
#endif
DEFINE_NETWORK_GAME_MESSAGE(message_client_add_player_request_ingame, 0x20); DEFINE_NETWORK_GAME_MESSAGE(message_client_add_player_request_ingame, 0x20);
DEFINE_NETWORK_GAME_MESSAGE(message_client_remove_player_request_ingame, 0x20); DEFINE_NETWORK_GAME_MESSAGE(message_client_remove_player_request_ingame, 0x20);
DEFINE_NETWORK_GAME_MESSAGE(message_client_host_crashed_cry_for_help, 0x10); DEFINE_NETWORK_GAME_MESSAGE(message_client_host_crashed_cry_for_help, 0x10);
@@ -469,6 +482,12 @@ struct network_game_message_packet_definitions data_0030aa68 =
DATA_PACKET_FIELD(_data_packet_field_shorts, 3), DATA_PACKET_FIELD(_data_packet_field_shorts, 3),
DATA_PACKET_FIELD(_data_packet_field_pad, 2), DATA_PACKET_FIELD(_data_packet_field_pad, 2),
DATA_PACKET_FIELD_END, DATA_PACKET_FIELD_END,
#ifdef HALO_LINUX
DATA_PACKET_FIELD(_data_packet_field_array, 16),
DATA_PACKET_FIELD(_data_packet_field_shorts, 22),
DATA_PACKET_FIELD_END,
DATA_PACKET_FIELD(_data_packet_field_pad, 2),
#endif
DATA_PACKET_FIELD_END, DATA_PACKET_FIELD_END,
}, },
NETWORK_GAME_MESSAGE_DEFINITION(server_game_update, "message_server_game_update_packet", message_server_game_update), NETWORK_GAME_MESSAGE_DEFINITION(server_game_update, "message_server_game_update_packet", message_server_game_update),
@@ -505,6 +524,12 @@ struct network_game_message_packet_definitions data_0030aa68 =
DATA_PACKET_FIELD(_data_packet_field_shorts, 3), DATA_PACKET_FIELD(_data_packet_field_shorts, 3),
DATA_PACKET_FIELD(_data_packet_field_pad, 2), DATA_PACKET_FIELD(_data_packet_field_pad, 2),
DATA_PACKET_FIELD_END, DATA_PACKET_FIELD_END,
#ifdef HALO_LINUX
DATA_PACKET_FIELD(_data_packet_field_array, 4),
DATA_PACKET_FIELD(_data_packet_field_shorts, 22),
DATA_PACKET_FIELD_END,
DATA_PACKET_FIELD(_data_packet_field_pad, 2),
#endif
DATA_PACKET_FIELD_END, DATA_PACKET_FIELD_END,
}, },
NETWORK_GAME_MESSAGE_DEFINITION(client_game_update, "message_client_game_update_packet", message_client_game_update), NETWORK_GAME_MESSAGE_DEFINITION(client_game_update, "message_client_game_update_packet", message_client_game_update),
@@ -628,6 +628,9 @@ struct message_client_game_update
short unknown04; short unknown04;
short player_count; short player_count;
struct player_action actions[MAXIMUM_PLAYERS_PER_MACHINE]; struct player_action actions[MAXIMUM_PLAYERS_PER_MACHINE];
#ifdef HALO_LINUX
PLAYER_ACTION_VR_TAIL(MAXIMUM_PLAYERS_PER_MACHINE)
#endif
}; };
struct server_update struct server_update
@@ -645,6 +648,9 @@ struct message_server_game_update
word unknown0C; word unknown0C;
word player_count; word player_count;
byte player_updates[MAXIMUM_NETWORK_PLAYER_COUNT * PLAYER_UPDATE_SIZE]; byte player_updates[MAXIMUM_NETWORK_PLAYER_COUNT * PLAYER_UPDATE_SIZE];
#ifdef HALO_LINUX
PLAYER_ACTION_VR_TAIL(MAXIMUM_VR_PLAYERS_PER_UPDATE)
#endif
}; };
struct network_machine struct network_machine
@@ -1706,6 +1712,23 @@ void network_game_server_handle_client_update_packet(
} }
update_server_handle_client_update(machine->machine_index, actions); update_server_handle_client_update(machine->machine_index, actions);
#ifdef HALO_LINUX
{
/* its players' VR blocks (game/players.h) */
struct player_action_vr vr[MAXIMUM_PLAYERS_PER_MACHINE];
short entry;
csmemset(vr, 0, sizeof(vr));
for (entry = 0; entry < message_packet->vr_count && entry < MAXIMUM_PLAYERS_PER_MACHINE; entry++)
{
short slot = message_packet->vr_entries[entry].player;
if (slot >= 0 && slot < message_packet->player_count)
vr[slot] = message_packet->vr_entries[entry].vr;
}
update_server_handle_client_vr(machine->machine_index, vr);
}
#endif
machine->last_received_update_sequence_number = machine->last_received_update_sequence_number =
message_packet->update_number & CLIENT_UPDATE_SEQUENCE_NUMBER_MASK; message_packet->update_number & CLIENT_UPDATE_SEQUENCE_NUMBER_MASK;
@@ -1798,6 +1821,25 @@ void network_game_server_update_ticks(
game_update.player_updates, game_update.player_updates,
update.player_updates, update.player_updates,
update.player_count * PLAYER_UPDATE_SIZE); update.player_count * PLAYER_UPDATE_SIZE);
#ifdef HALO_LINUX
{
/* the VR players' blocks (game/players.h) */
struct player_action_vr const *vr = update_server_get_update_vr(update_number);
short player_index;
game_update.vr_count = 0;
game_update.vr_pad = 0;
for (player_index = 0; vr && player_index < (short)update.player_count; player_index++)
{
if (vr[player_index].flags && game_update.vr_count < MAXIMUM_VR_PLAYERS_PER_UPDATE)
{
game_update.vr_entries[game_update.vr_count].player = player_index;
game_update.vr_entries[game_update.vr_count].vr = vr[player_index];
game_update.vr_count++;
}
}
}
#endif
message = create_network_game_message( message = create_network_game_message(
_message_server_game_update, _message_server_game_update,
@@ -424,6 +424,7 @@ struct message_client_game_update
#ifdef HALO_LINUX #ifdef HALO_LINUX
/* a machine's players, which 16 players / 4 machines only happened to give */ /* a machine's players, which 16 players / 4 machines only happened to give */
struct player_action actions[MAXIMUM_LOCAL_PLAYERS]; struct player_action actions[MAXIMUM_LOCAL_PLAYERS];
PLAYER_ACTION_VR_TAIL(MAXIMUM_LOCAL_PLAYERS)
#else #else
struct player_action actions[MAXIMUM_NUMBER_OF_PLAYERS / MAXIMUM_NETWORK_MACHINE_COUNT]; struct player_action actions[MAXIMUM_NUMBER_OF_PLAYERS / MAXIMUM_NETWORK_MACHINE_COUNT];
#endif #endif
+33 -36
View File
@@ -1013,8 +1013,8 @@ short unit_update_animation(
static boolean unit_vectors_are_valid(long unit_index); static boolean unit_vectors_are_valid(long unit_index);
static void unit_throw_grenade_release(long unit_index, boolean premature); static void unit_throw_grenade_release(long unit_index, boolean premature);
#ifdef HALO_ANDROID #ifdef HALO_LINUX
static short unit_vr_grenade_player(long unit_index); static struct player_action_vr const *unit_vr_action(long unit_index);
#endif #endif
static void unit_seat_update(long object_index); static void unit_seat_update(long object_index);
@@ -5783,14 +5783,14 @@ boolean unit_update(
break; break;
case _unit_grenade_throw_in_hand: case _unit_grenade_throw_in_hand:
++unit->unit.grenade_throw_ticks; ++unit->unit.grenade_throw_ticks;
#ifdef HALO_ANDROID #ifdef HALO_LINUX
{ {
/* VR: held while the trigger is, and thrown as it lets go */ /* VR: held while the trigger is, and thrown as it lets go */
short vr_local_player_index = unit_vr_grenade_player(unit_index); struct player_action_vr const *vr_action = unit_vr_action(unit_index);
if (vr_local_player_index != NONE) if (vr_action)
{ {
if (!halo_vr_grenade_held(vr_local_player_index)) if (!TEST_FLAG(vr_action->flags, _player_action_vr_grenade_held_bit))
unit_throw_grenade_release(unit_index, FALSE); unit_throw_grenade_release(unit_index, FALSE);
break; break;
} }
@@ -8948,14 +8948,16 @@ void unit_cause_player_melee_damage(
} }
} }
#ifdef HALO_ANDROID #ifdef HALO_LINUX
if (unit->unit.player_index!=NONE)
{ {
/* VR: the swing's weight, not the run's */ /* VR: the swing's weight, not the run's */
real swing = halo_vr_melee_strength(); struct player_action_vr const *vr_action = unit_vr_action(unit_index);
if (swing>damage_data.scale) if (vr_action && TEST_FLAG(vr_action->flags, _player_action_vr_melee_bit) &&
damage_data.scale = swing; player_action_vr_direction(vr_action->melee_strength)>damage_data.scale)
{
damage_data.scale = player_action_vr_direction(vr_action->melee_strength);
}
} }
#endif #endif
if (unit->object.type==_object_type_biped && if (unit->object.type==_object_type_biped &&
@@ -11096,19 +11098,17 @@ static boolean unit_vectors_are_valid(
return result; return result;
} }
#ifdef HALO_ANDROID #ifdef HALO_LINUX
/* VR: the local player whose grenade the left hand holds and throws /* VR: the VR block of the input of the player whose unit this is
(halo_vr_grenade_held), or NONE */ (game/players.h), or NULL: the same on every machine in a game */
static short unit_vr_grenade_player( static struct player_action_vr const *unit_vr_action(
long unit_index) long unit_index)
{ {
long player_index = player_index_from_unit_index(unit_index); long player_index = player_index_from_unit_index(unit_index);
struct player_datum *player;
if (!halo_vr_active() || player_index == NONE) if (player_index == NONE || player_get(player_index)->unit_index != unit_index)
return NONE; return NULL;
player = player_get(player_index); return player_action_vr_get(player_index);
return player->unit_index == unit_index ? player->local_player_index : NONE;
} }
#endif #endif
@@ -11120,12 +11120,12 @@ static void unit_throw_grenade_release(
struct unit_datum *unit = unit_get(unit_index); struct unit_datum *unit = unit_get(unit_index);
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index); struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
#ifdef HALO_ANDROID #ifdef HALO_LINUX
short vr_local_player_index = unit_vr_grenade_player(unit_index); struct player_action_vr const *vr_action = unit_vr_action(unit_index);
boolean vr_thrown = FALSE; boolean vr_thrown = FALSE;
/* VR: the grenade stays in the hand while the trigger holds it */ /* VR: the grenade stays in the hand while the trigger holds it */
if (vr_local_player_index != NONE && halo_vr_grenade_held(vr_local_player_index)) if (vr_action && TEST_FLAG(vr_action->flags, _player_action_vr_grenade_held_bit))
return; return;
#endif #endif
@@ -11182,24 +11182,21 @@ static void unit_throw_grenade_release(
{ {
real scale = unit_definition->unit.grenade_velocity/TICKS_PER_SECOND; real scale = unit_definition->unit.grenade_velocity/TICKS_PER_SECOND;
#ifdef HALO_ANDROID #ifdef HALO_LINUX
float vr_origin[3], vr_direction[3], vr_strength;
/* VR: thrown by the hand, from it, its way and as hard */ /* VR: thrown by the hand, from it, its way and as hard */
if (vr_local_player_index != NONE && if (vr_action && TEST_FLAG(vr_action->flags, _player_action_vr_grenade_thrown_bit))
halo_vr_grenade_throw(vr_local_player_index, vr_origin, vr_direction, &vr_strength))
{ {
real_point3d origin; real_point3d origin;
real_vector3d direction; real_vector3d direction;
origin.x = vr_origin[0]; unit_get_camera_position(unit_index, &origin);
origin.y = vr_origin[1]; origin.x += player_action_vr_offset(vr_action->grenade_offset[0]);
origin.z = vr_origin[2]; origin.y += player_action_vr_offset(vr_action->grenade_offset[1]);
direction.i = vr_direction[0]; origin.z += player_action_vr_offset(vr_action->grenade_offset[2]);
direction.j = vr_direction[1]; player_action_vr_get_direction(vr_action->grenade_direction, &direction);
direction.k = vr_direction[2];
object_translate(grenade_index, &origin, NULL); object_translate(grenade_index, &origin, NULL);
scale_vector3d(&direction, scale * vr_strength, &initial_velocity); scale_vector3d(&direction,
scale * player_action_vr_direction(vr_action->grenade_strength), &initial_velocity);
vr_thrown = TRUE; vr_thrown = TRUE;
} }
else else
@@ -11208,7 +11205,7 @@ static void unit_throw_grenade_release(
} }
} }
#ifdef HALO_ANDROID #ifdef HALO_LINUX
if (vr_thrown) if (vr_thrown)
premature = FALSE; premature = FALSE;
#endif #endif