Co-Op Layer
This commit is contained in:
@@ -63,7 +63,7 @@ namespace ProjectM.Client
|
||||
SystemAPI.GetSingletonEntity<GhostCollection>(),
|
||||
SystemAPI.GetSingletonEntity<SpawnedGhostEntityMap>());
|
||||
|
||||
m_PredictedGhostSpawnLookup = state.GetBufferLookup<PredictedGhostSpawn>(true);
|
||||
m_PredictedGhostSpawnLookup = state.GetBufferLookup<PredictedGhostSpawn>(false);
|
||||
m_ProjectileLookup = state.GetComponentLookup<Projectile>(true);
|
||||
|
||||
state.RequireForUpdate<GhostSpawnQueue>();
|
||||
@@ -134,7 +134,7 @@ namespace ProjectM.Client
|
||||
// is NOT marked [ReadOnly].
|
||||
public Entity PredictedSpawnListEntity;
|
||||
|
||||
[ReadOnly] public BufferLookup<PredictedGhostSpawn> PredictedGhostSpawnLookup;
|
||||
public BufferLookup<PredictedGhostSpawn> PredictedGhostSpawnLookup;
|
||||
[ReadOnly] public ComponentLookup<Projectile> ProjectileLookup;
|
||||
|
||||
// 'data' is taken by value (NOT 'in') because TryGetComponentDataFromSnapshotHistory needs a
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.NetCode;
|
||||
using Unity.Networking.Transport;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Client/thin-client half of the M4 LAN join flow. When the per-world <see cref="ConnectionConfig"/>
|
||||
/// requests <see cref="ConnectionMode.Join"/>, creates a <see cref="NetworkStreamRequestConnect"/>
|
||||
/// entity for the parsed endpoint and clears the request. Runs in both full and thin client worlds so
|
||||
/// the editor auto-host can connect thin clients too. Not Burst-compiled: <c>NetworkEndpoint.TryParse</c>
|
||||
/// takes a managed string and this is a cold path.
|
||||
/// </summary>
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ThinClientSimulation)]
|
||||
public partial struct ClientConnectionControlSystem : ISystem
|
||||
{
|
||||
public void OnCreate(ref SystemState state)
|
||||
{
|
||||
state.RequireForUpdate<ConnectionConfig>();
|
||||
}
|
||||
|
||||
public void OnUpdate(ref SystemState state)
|
||||
{
|
||||
var cfgRef = SystemAPI.GetSingletonRW<ConnectionConfig>();
|
||||
if (!cfgRef.ValueRO.Requested || cfgRef.ValueRO.Mode != ConnectionMode.Join)
|
||||
return;
|
||||
|
||||
// Clear first so a malformed address does not retry-spam every frame.
|
||||
cfgRef.ValueRW.Requested = false;
|
||||
|
||||
var address = cfgRef.ValueRO.Address;
|
||||
var port = cfgRef.ValueRO.Port;
|
||||
if (!NetworkEndpoint.TryParse(address.ToString(), port, out var endpoint, NetworkFamily.Ipv4))
|
||||
{
|
||||
Debug.LogError($"[ClientConnectionControlSystem] Invalid join address '{address}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
||||
var req = ecb.CreateEntity();
|
||||
ecb.AddComponent(req, new NetworkStreamRequestConnect { Endpoint = endpoint });
|
||||
ecb.Playback(state.EntityManager);
|
||||
ecb.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c274f15ad41814f8d8337f786590313d
|
||||
@@ -0,0 +1,79 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using Unity.NetCode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimal IMGUI Host / Join (+ IP) panel for the M4 LAN flow — the connection entry point for player
|
||||
/// builds, where there is no editor auto-host. Writes a <see cref="ConnectionConfig"/> request into the
|
||||
/// relevant netcode world(s): <b>Host</b> makes the server world listen and the local client join over
|
||||
/// loopback; <b>Join</b> makes the client world connect to the typed IP. Hides itself once the client
|
||||
/// world has a connection. Direct IP/LAN only — Unity Relay is deferred to a later pass. In the editor
|
||||
/// the <c>EditorAutoHostSystem</c> usually connects first, so this panel just shows "Connected".
|
||||
/// </summary>
|
||||
public class ConnectionUI : MonoBehaviour
|
||||
{
|
||||
[SerializeField] ushort _port = 7979;
|
||||
|
||||
string _ipField = "127.0.0.1";
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (IsClientConnected())
|
||||
{
|
||||
GUILayout.BeginArea(new Rect(10, 10, 220, 26));
|
||||
GUILayout.Label("Net: Connected");
|
||||
GUILayout.EndArea();
|
||||
return;
|
||||
}
|
||||
|
||||
GUILayout.BeginArea(new Rect(10, 10, 240, 150), GUI.skin.box);
|
||||
GUILayout.Label("Project M — LAN co-op");
|
||||
|
||||
// Host is only possible where a server world exists (editor or a ClientServer/host build).
|
||||
if (ClientServerBootstrap.ServerWorld is { IsCreated: true })
|
||||
{
|
||||
if (GUILayout.Button("Host"))
|
||||
{
|
||||
Seed(ClientServerBootstrap.ServerWorld, ConnectionMode.Host, "0.0.0.0", _port);
|
||||
Seed(ClientServerBootstrap.ClientWorld, ConnectionMode.Join, "127.0.0.1", _port);
|
||||
}
|
||||
GUILayout.Space(6);
|
||||
}
|
||||
|
||||
GUILayout.Label("Join host IP:");
|
||||
_ipField = GUILayout.TextField(_ipField);
|
||||
if (GUILayout.Button("Join"))
|
||||
Seed(ClientServerBootstrap.ClientWorld, ConnectionMode.Join, _ipField, _port);
|
||||
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
|
||||
static bool IsClientConnected()
|
||||
{
|
||||
var world = ClientServerBootstrap.ClientWorld;
|
||||
if (world is not { IsCreated: true })
|
||||
return false;
|
||||
using var q = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<NetworkId>());
|
||||
return !q.IsEmpty;
|
||||
}
|
||||
|
||||
static void Seed(World world, ConnectionMode mode, string address, ushort port)
|
||||
{
|
||||
if (world is not { IsCreated: true })
|
||||
return;
|
||||
var em = world.EntityManager;
|
||||
using var q = em.CreateEntityQuery(ComponentType.ReadWrite<ConnectionConfig>());
|
||||
Entity e = q.IsEmpty ? em.CreateEntity(typeof(ConnectionConfig)) : q.GetSingletonEntity();
|
||||
em.SetComponentData(e, new ConnectionConfig
|
||||
{
|
||||
Mode = mode,
|
||||
Address = address,
|
||||
Port = port,
|
||||
Requested = true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 977a7574cf6ff4898b7e9db6c21368f9
|
||||
@@ -10,7 +10,8 @@
|
||||
"Unity.Burst",
|
||||
"Unity.NetCode",
|
||||
"Unity.Entities.Graphics",
|
||||
"Unity.InputSystem"
|
||||
"Unity.InputSystem",
|
||||
"Unity.Networking.Transport"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
Reference in New Issue
Block a user