Netcode Bootstrap

This commit is contained in:
Luis Gonzalez
2026-05-31 14:27:52 -07:00
parent 99d8d2d2a9
commit 7fa77ce821
1813 changed files with 2921554 additions and 84 deletions
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2913bc88b49f3421c82223b996a66a4b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,47 @@
using ProjectM.Simulation;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.NetCode;
namespace ProjectM.Client
{
/// <summary>
/// Client-side connection handshake: for every connection that has been assigned a
/// <see cref="NetworkId"/> but is not yet <see cref="NetworkStreamInGame"/>, mark it in-game and
/// fire a <see cref="GoInGameRequest"/> RPC so the server spawns this client's player ghost.
/// Adding NetworkStreamInGame is what gates snapshot/command flow on. Mirrors the netcode
/// "networked-cube" go-in-game sample.
/// </summary>
[BurstCompile]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ThinClientSimulation)]
public partial struct GoInGameClientSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
var builder = new EntityQueryBuilder(Allocator.Temp)
.WithAll<NetworkId>()
.WithNone<NetworkStreamInGame>();
state.RequireForUpdate(state.GetEntityQuery(builder));
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(Allocator.Temp);
foreach (var (_, connection) in
SystemAPI.Query<RefRO<NetworkId>>().WithNone<NetworkStreamInGame>().WithEntityAccess())
{
ecb.AddComponent<NetworkStreamInGame>(connection);
var request = ecb.CreateEntity();
ecb.AddComponent<GoInGameRequest>(request);
ecb.AddComponent(request, new SendRpcCommandRequest { TargetConnection = connection });
}
ecb.Playback(state.EntityManager);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e2b0386021d7340d0b023ab1d954ce14
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bb249fc7c6daa49478fb318eb553b719
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,68 @@
using ProjectM.Simulation;
using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;
namespace ProjectM.Client
{
/// <summary>
/// Client-only twin-stick input gather. Samples the Input System once per frame (WASD /
/// left-stick -&gt; Move, right-stick -&gt; Aim) and writes <see cref="PlayerInput"/> on the
/// locally-owned player ghost (filtered to <see cref="GhostOwnerIsLocal"/>). Runs in
/// <see cref="GhostInputSystemGroup"/> — NOT the prediction loop — so devices are read once per
/// frame, never re-read during rollback. Implemented as a non-Burst <see cref="ISystem"/>
/// because it reads the managed Input System.
/// <para>
/// NOTE: the Input System device types are fully qualified rather than imported via
/// <c>using UnityEngine.InputSystem;</c> on purpose — that namespace also defines a
/// <c>PlayerInput</c> type which would collide with <see cref="ProjectM.Simulation.PlayerInput"/>
/// and make the Entities source generator bind <c>RefRW&lt;PlayerInput&gt;</c> to the managed
/// class (a spurious CS8377 "must be unmanaged").
/// </para>
/// </summary>
[UpdateInGroup(typeof(GhostInputSystemGroup))]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public partial struct PlayerInputGatherSystem : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<PlayerInput>();
}
public void OnUpdate(ref SystemState state)
{
float2 move = float2.zero;
float2 aim = float2.zero;
var keyboard = UnityEngine.InputSystem.Keyboard.current;
if (keyboard != null)
{
if (keyboard.wKey.isPressed) move.y += 1f;
if (keyboard.sKey.isPressed) move.y -= 1f;
if (keyboard.dKey.isPressed) move.x += 1f;
if (keyboard.aKey.isPressed) move.x -= 1f;
}
var gamepad = UnityEngine.InputSystem.Gamepad.current;
if (gamepad != null)
{
float2 leftStick = gamepad.leftStick.ReadValue();
if (math.lengthsq(leftStick) > math.lengthsq(move))
move = leftStick;
aim = gamepad.rightStick.ReadValue();
}
// Right-stick deadzone: a resting stick yields zero Aim so PlayerAimSystem falls back to
// the movement heading (controller-first directional aim).
if (math.lengthsq(aim) < 0.04f)
aim = float2.zero;
foreach (var input in SystemAPI.Query<RefRW<PlayerInput>>().WithAll<GhostOwnerIsLocal>())
{
input.ValueRW.Move = move;
input.ValueRW.Aim = aim;
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 27c80caff08534f239e39ff4732fbdfd
@@ -8,7 +8,8 @@
"Unity.Mathematics",
"Unity.Burst",
"Unity.NetCode",
"Unity.Entities.Graphics"
"Unity.Entities.Graphics",
"Unity.InputSystem"
],
"includePlatforms": [],
"excludePlatforms": [],