该项目的目的是同时测试和演示来自 Unity DOTS 技术堆栈的多个新包。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

886 行
32 KiB

using Unity.Collections;
using UnityEngine;
using Unity.Entities;
using Unity.Jobs;
using Unity.Networking.Transport;
using Unity.Scenes;
using UnityEngine.Profiling;
using Unity.NetCode;
using Unity.Sample.Core;
class A2DotsShooterSendSystem : CommandSendSystem<UserCommand>
{
}
class A2DotsShooterRecvSystem : CommandReceiveSystem<UserCommand>
{
}
[UpdateInGroup(typeof(ClientSimulationSystemGroup))]
[UpdateBefore(typeof(GhostSimulationSystemGroup))]
[UpdateBefore(typeof(A2DotsShooterSendSystem))]
[AlwaysUpdateSystem]
[AlwaysSynchronizeSystem]
public class BeforeClientPredictionSystem : JobComponentSystem
{
public ClientGameWorld GameWorld;
public int TickRate;
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
var tickRate = default(ClientServerTickRate);
if (HasSingleton<ClientServerTickRate>())
tickRate = GetSingleton<ClientServerTickRate>();
tickRate.ResolveDefaults();
TickRate = tickRate.SimulationTickRate;
if (GameWorld != null)
GameWorld.BeforePredictionUpdate(Time.DeltaTime, HasSingleton<ThinClientComponent>());
return default;
}
}
[UpdateInGroup(typeof(ClientSimulationSystemGroup))]
[UpdateAfter(typeof(GhostSimulationSystemGroup))]
[UpdateBefore(typeof(Unity.Animation.AnimationSystemGroup))]
[AlwaysUpdateSystem]
[AlwaysSynchronizeSystem]
public class AfterClientPredictionSystem : JobComponentSystem
{
public ClientGameWorld GameWorld;
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
if (GameWorld != null && !HasSingleton<ThinClientComponent>())
GameWorld.AfterPredictionUpdate();
return default;
}
}
[UpdateInGroup(typeof(GhostPredictionSystemGroup))]
[AlwaysUpdateSystem]
[AlwaysSynchronizeSystem]
public class ClientPredictionSystem : JobComponentSystem
{
public ClientGameWorld GameWorld;
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
if (GameWorld != null && !HasSingleton<ThinClientComponent>())
GameWorld.PredictionUpdate(World.GetExistingSystem<GhostPredictionSystemGroup>().PredictingTick, Time.DeltaTime);
return default;
}
}
[UpdateInGroup(typeof(ClientPresentationSystemGroup))]
[AlwaysUpdateSystem]
[AlwaysSynchronizeSystem]
public class ClientLateUpdateSystem : JobComponentSystem
{
public ClientGameWorld GameWorld;
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
if (GameWorld != null && !HasSingleton<ThinClientComponent>())
GameWorld.LateUpdate(World.GetExistingSystem<ChatSystemClient>(), Time.DeltaTime);
return default;
}
}
public class ClientGameWorld
{
public ClientGameWorld(World world, int localPlayerId)
{
m_GameWorld = world;
m_PlayerModule = new PlayerModuleClient(m_GameWorld);
m_HandleDamageGroup = m_GameWorld.CreateSystem<HandleDamageSystemGroup>();
m_HandleControlledEntityChangedGroup = m_GameWorld.CreateSystem<ManualComponentSystemGroup>();
m_HandleControlledEntityChangedGroup.AddSystemToUpdateList(m_GameWorld.CreateSystem<PlayerCharacterControl.PlayerCharacterControlSystem>());
m_PredictedUpdateGroup = m_GameWorld.CreateSystem<ManualComponentSystemGroup>();
m_PredictedUpdateGroup.AddSystemToUpdateList(CharacterModule.CreateClientUpdateSystemGroup(world));
m_PredictedUpdateGroup.AddSystemToUpdateList(world.CreateSystem<AbilityUpdateSystemGroup>());
m_AfterPredictionUpdateGroup = m_GameWorld.CreateSystem<ManualComponentSystemGroup>();
m_AfterPredictionUpdateGroup.AddSystemToUpdateList(m_GameWorld.GetOrCreateSystem(typeof(PartSystemUpdateGroup)));
m_AfterPredictionUpdateGroup.AddSystemToUpdateList(CharacterModule.CreateClientPresentationSystemGroup(world));
m_AfterPredictionUpdateGroup.AddSystemToUpdateList(world.CreateSystem<UpdateCharacterUI>());
m_GameModeSystem = m_GameWorld.CreateSystem<GameModeSystemClient>();
m_ClientFrontendUpdate = m_GameWorld.CreateSystem<ClientFrontendUpdate>();
m_TeleporterSystemClient = m_GameWorld.CreateSystem<TeleporterSystemClient>();
m_ClientLateUpdate = m_GameWorld.CreateSystem<ClientLateUpdateGroup>();
m_GameModeSystem.SetLocalPlayerId(localPlayerId);
m_controlledEntityCameraUpdate = m_GameWorld.GetOrCreateSystem<ControlledEntityCameraUpdate>();
m_controlledEntityCameraUpdate.SortSystemUpdateList();// TODO (mogensh) currently needed because of bug in entities preview.26
//@TODO: Temp hack for unite keynote to hide error
Debug.developerConsoleVisible = false;
}
public void Shutdown(bool isDestroyingWorld)
{
m_PlayerModule.Shutdown();
// When destroying the world all systems will be torn down - so no need to do it manually
if (!isDestroyingWorld)
{
m_HandleDamageGroup.DestroyGroup();
m_GameWorld.DestroySystem(m_HandleControlledEntityChangedGroup);
m_GameWorld.DestroySystem(m_PredictedUpdateGroup);
m_GameWorld.DestroySystem(m_AfterPredictionUpdateGroup);
m_GameWorld.DestroySystem(m_GameModeSystem);
m_GameWorld.DestroySystem(m_TeleporterSystemClient);
m_GameWorld.DestroySystem(m_ClientLateUpdate);
m_controlledEntityCameraUpdate.DestroyGroup();
}
AnimationGraphHelper.Shutdown(m_GameWorld);
}
public void BeforePredictionUpdate(float frameDuration, bool thinClient)
{
// Advances time and accumulate input into the UserCommand being generated
HandleTime(frameDuration, thinClient);
var gameTimeSystem = m_GameWorld.GetExistingSystem<GameTimeSystem>();
gameTimeSystem.SetWorldTime(m_RenderTime);
gameTimeSystem.frameDuration = frameDuration;
if (thinClient)
return;
m_PlayerModule.ResolveReferenceFromLocalPlayerToPlayer();
m_PlayerModule.HandleCommandReset();
// Handle spawning
m_PlayerModule.HandleSpawn();
// Handle controlled entity changed
m_HandleControlledEntityChangedGroup.Update();
m_PlayerModule.HandleControlledEntityChanged();
// Prediction
gameTimeSystem.SetWorldTime(m_PredictedTime);
}
public void AfterPredictionUpdate()
{
var gameTimeSystem = m_GameWorld.GetExistingSystem<GameTimeSystem>();
// TODO (timj) is this needed?
gameTimeSystem.SetWorldTime(m_PredictedTime);
m_PlayerModule.RetrieveCommand((uint)gameTimeSystem.GetWorldTime().tick);
m_GameModeSystem.Update();
// Update Presentation
gameTimeSystem.SetWorldTime(m_PredictedTime);
m_TeleporterSystemClient.Update();
// TODO (mogensh) for now we upadte this AFTER CharacterModule as we depend on AnimSourceCtrl to run before bodypart. Sort this out
m_AfterPredictionUpdateGroup.Update();
gameTimeSystem.SetWorldTime(m_RenderTime);
m_HandleDamageGroup.Update();
//#if UNITY_EDITOR
//
// var localPlayerState = m_GameWorld.GetEntityManager().GetComponentObject<LocalPlayer>(m_localPlayer);
//
// if (m_GameWorld.GetEntityManager().Exists(localPlayerState.controlledEntity) &&
// m_GameWorld.GetEntityManager().HasComponent<PlayerControlled.State>(localPlayerState.controlledEntity))
// {
// var userCommand = m_GameWorld.GetEntityManager().GetComponentData<PlayerControlled.State>(localPlayerState.controlledEntity);
// m_ReplicatedEntityModule.FinalizedStateHistory(m_PredictedTime.tick - 1, m_NetworkClient.serverTime, ref userCommand.command);
// }
//#endif
}
public void LateUpdate(ChatSystemClient chatSystem, float frameDuration)
{
var gameTimeSystem = m_GameWorld.GetExistingSystem<GameTimeSystem>();
gameTimeSystem.SetWorldTime(m_RenderTime);
var localPlayerState = m_GameWorld.EntityManager.GetComponentData<LocalPlayer>(m_localPlayer);
var teamId = -1;
bool showScorePanel = false;
if (localPlayerState.playerEntity != Entity.Null)
{
var playerState = m_GameWorld.EntityManager.GetComponentData<Player.State>(localPlayerState.playerEntity);
if (playerState.controlledEntity != Entity.Null)
{
teamId = playerState.teamIndex;
if (m_GameWorld.EntityManager.HasComponent<HealthStateData>(playerState.controlledEntity))
{
var healthState = m_GameWorld.EntityManager.GetComponentData<HealthStateData>(playerState.controlledEntity);
// Only show score board when alive
showScorePanel = healthState.health <= 0;
}
}
}
// TODO (petera) fix this hack
chatSystem.UpdateLocalTeamIndex(teamId);
m_ClientLateUpdate.Update();
m_controlledEntityCameraUpdate.Update();
m_PlayerModule.CameraUpdate();
gameTimeSystem.SetWorldTime(m_RenderTime);
gameTimeSystem.SetWorldTime(m_PredictedTime);
if (Game.game.clientFrontend != null)
{
m_ClientFrontendUpdate.Update();
Game.game.clientFrontend.SetShowScorePanel(showScorePanel);
}
}
public Entity RegisterLocalPlayer(int playerId)
{
m_localPlayer = m_PlayerModule.RegisterLocalPlayer(playerId);
return m_localPlayer;
}
public void PredictionUpdate(uint tick, float deltaTime)
{
var gameTimeSystem = m_GameWorld.GetExistingSystem<GameTimeSystem>();
var time = gameTimeSystem.GetWorldTime();
// TODO (timj) tick should be uint
time.SetTime((int)tick, deltaTime);
gameTimeSystem.SetWorldTime(time);
m_PlayerModule.RetrieveCommand(tick);
gameTimeSystem.SetWorldTime(time);
m_PredictedUpdateGroup.Update();
}
void HandleTime(float frameDuration, bool thinClient)
{
var simGroup = m_GameWorld.GetExistingSystem<ClientSimulationSystemGroup>();
// Sample input into current command
// The time passed in here is used to calculate the amount of rotation from stick position
// The command stores final view direction
bool chatOpen = Game.game.clientFrontend != null && Game.game.clientFrontend.chatPanel.isOpen;
bool userInputEnabled = InputSystem.GetMousePointerLock() && !chatOpen && !thinClient;
if (simGroup.ServerTick != m_lastCommandTick)
{
m_lastCommandTick = simGroup.ServerTick;
m_PlayerModule.ResetInput(userInputEnabled);
}
m_PlayerModule.SampleInput(m_GameWorld, userInputEnabled, frameDuration, m_RenderTime.tick);
var serverTickRate = m_GameWorld.GetExistingSystem<BeforeClientPredictionSystem>().TickRate;
m_PredictedTime.tickRate = serverTickRate;
m_RenderTime.tickRate = serverTickRate;
// TODO (timj) tick should be uint for dots netcode
m_RenderTime.tick = (int)simGroup.InterpolationTick;
m_RenderTime.tickDuration = frameDuration;
m_PredictedTime.tick = (int) simGroup.ServerTick;
m_PredictedTime.tickDuration = frameDuration;
m_PlayerModule.StoreCommand(m_PredictedTime.tick);
}
World m_GameWorld;
GameTime m_PredictedTime = new GameTime(60);
GameTime m_RenderTime = new GameTime(60);
// External systems
ClientFrontendUpdate m_ClientFrontendUpdate;
// Internal systems
readonly PlayerModuleClient m_PlayerModule;
readonly HandleDamageSystemGroup m_HandleDamageGroup;
readonly ManualComponentSystemGroup m_HandleControlledEntityChangedGroup;
readonly ManualComponentSystemGroup m_PredictedUpdateGroup;
readonly ManualComponentSystemGroup m_AfterPredictionUpdateGroup;
readonly GameModeSystemClient m_GameModeSystem;
readonly ControlledEntityCameraUpdate m_controlledEntityCameraUpdate;
private readonly ClientLateUpdateGroup m_ClientLateUpdate;
readonly TeleporterSystemClient m_TeleporterSystemClient;
Entity m_localPlayer;
uint m_lastCommandTick;
}
public class ClientGameLoop : Game.IGameLoop
{
// Client vars
[ConfigVar(Name = "client.updaterate", DefaultValue = "30000", Description = "Max bytes/sec client wants to receive", Flags = ConfigVar.Flags.ClientInfo)]
public static ConfigVar clientUpdateRate;
[ConfigVar(Name = "client.updateinterval", DefaultValue = "3", Description = "Snapshot sendrate requested by client", Flags = ConfigVar.Flags.ClientInfo)]
public static ConfigVar clientUpdateInterval;
[ConfigVar(Name = "client.playername", DefaultValue = "", Description = "Name of player", Flags = ConfigVar.Flags.ClientInfo | ConfigVar.Flags.Save)]
public static ConfigVar clientPlayerName;
public static string[] CurrentArgs;
private World m_World;
private bool m_NewWorld;
public bool Init(string[] args)
{
// TODO (timj) create singleton for parameters instead
CurrentArgs = args;
m_World = ClientServerBootstrap.CreateClientWorld(GameBootStrap.DefaultWorld, "ClientWorld0");
m_World.GetExistingSystem<ClientGameLoopSystem>().GameLoop = this;
CurrentArgs = null;
return true;
}
public void Shutdown()
{
// TODO (timj) world shutdown order - the ecs worlds are already torn down when application quit is called
if (m_World == null)
return;
m_World.GetExistingSystem<ClientGameLoopSystem>().Shutdown();
m_World.Dispose();
}
public void Update()
{
if (m_NewWorld)
{
// TODO (timj) copy some state from the world and restore after recreating it
m_World.Dispose();
Init(null);
}
}
public void FixedUpdate()
{
}
public void LateUpdate()
{
}
public void CmdConnect(string[] args)
{
m_World.GetExistingSystem<ClientGameLoopSystem>().CmdConnect(args);
}
public void RequestNewWorld()
{
m_NewWorld = true;
}
}
[UpdateInGroup(typeof(ClientInitializationSystemGroup))]
[AlwaysUpdateSystem]
public class ClientGameLoopSystem : ComponentSystem
{
private Entity netCodePrefabs;
private EntityQuery m_ConnectionQuery;
private NetworkStreamReceiveSystem m_NetworkStreamReceiveSystem;
public ClientGameLoop GameLoop;
public static Unity.Entities.Hash128 ClientBuildSettingsGUID => new Unity.Entities.Hash128("53329cb030ec042419e0a8b2387da5c7");
public void Shutdown()
{
m_StateMachine.SwitchTo(World, ClientState.Browsing);
}
protected override void OnCreate()
{
var args = ClientGameLoop.CurrentArgs;
if (args == null)
{
#if UNITY_EDITOR
if (GameBootStrap.IsSingleLevelPlaymode)
args = new[] {ClientServerBootstrap.RequestedAutoConnect};
else
#endif
args = new string[0];
}
World.GetOrCreateSystem<SceneSystem>().BuildSettingsGUID = ClientBuildSettingsGUID;
netCodePrefabs = Entity.Null;
m_NetworkStreamReceiveSystem = World.GetOrCreateSystem<NetworkStreamReceiveSystem>();
m_ConnectionQuery = EntityManager.CreateEntityQuery(ComponentType.ReadWrite<NetworkStreamConnection>());
m_StateMachine = new StateMachine<ClientState>();
m_StateMachine.Add(ClientState.Browsing, EnterBrowsingState, UpdateBrowsingState, LeaveBrowsingState);
m_StateMachine.Add(ClientState.Connecting, EnterConnectingState, UpdateConnectingState, null);
m_StateMachine.Add(ClientState.Loading, EnterLoadingState, UpdateLoadingState, null);
m_StateMachine.Add(ClientState.WaitSubscene,null, UpdateWaitSubsceneState, null);
m_StateMachine.Add(ClientState.Playing, EnterPlayingState, UpdatePlayingState, LeavePlayingState);
m_GameWorld = World;
World.CreateSystem<GameTimeSystem>();
GameDebug.Log("Network client initialized");
if(ClientGameLoop.clientPlayerName.Value.Length == 0)
{
// Pick silly default name if none set
ClientGameLoop.clientPlayerName.Value =
new string[] { "Shiny", "Jumpy", "Fast", "Snappy", "Smooth", "First" }[Random.Range(0, 6)] + " " +
new string[] { "Entity", "Component", "Buffer", "System", "Chunk", "Array" }[Random.Range(0, 6)];
}
m_requestedPlayerSettings.playerName = ClientGameLoop.clientPlayerName.Value;
m_requestedPlayerSettings.teamId = -1;
Console.AddCommand("disconnect", CmdDisconnect, "Disconnect from server if connected", this.GetHashCode());
Console.AddCommand("runatserver", CmdRunAtServer, "Run command at server", this.GetHashCode());
Console.AddCommand("respawn", CmdRespawn, "Force a respawn", this.GetHashCode());
Console.AddCommand("nextchar", CmdNextChar, "Select next character", this.GetHashCode());
Console.AddCommand("nextteam", CmdNextTeam, "Select next character", this.GetHashCode());
if (args.Length > 0)
{
targetServer = args[0];
m_StateMachine.SwitchTo(m_GameWorld, ClientState.Connecting);
}
else
m_StateMachine.SwitchTo(m_GameWorld, ClientState.Browsing);
GameDebug.Log("Client initialized");
}
protected override void OnDestroy()
{
m_isDestroyingWorld = true;
GameDebug.Log("ClientGameLoop shutdown");
Console.RemoveCommandsWithTag(this.GetHashCode());
m_StateMachine.Shutdown();
PrefabAssetManager.Shutdown();
}
bool IsConnected()
{
return m_ConnectionQuery.CalculateEntityCount() == 1 &&
m_ConnectionQuery.GetSingleton<NetworkStreamConnection>().Value.GetState(m_NetworkStreamReceiveSystem.Driver) ==
NetworkConnection.State.Connected;
}
void Disconnect()
{
m_NetworkStreamReceiveSystem.Driver.Disconnect(m_ConnectionQuery.GetSingleton<NetworkStreamConnection>().Value);
m_NetworkStreamReceiveSystem.EntityManager.AddComponent<NetworkStreamDisconnected>(m_ConnectionQuery.GetSingletonEntity());
}
protected override void OnUpdate()
{
Profiler.BeginSample("-StateMachine update");
m_StateMachine.Update();
Profiler.EndSample();
// TODO (petera) change if we have a lobby like setup one day
if (m_StateMachine.CurrentState() == ClientState.Playing && Game.game.clientFrontend != null)
Game.game.clientFrontend.UpdateChat(World.GetExistingSystem<ChatSystemClient>());
// TODO (petera) merge with clientinfo
if (m_requestedPlayerSettings.playerName != ClientGameLoop.clientPlayerName.Value)
{
// Cap name length
ClientGameLoop.clientPlayerName.Value = ClientGameLoop.clientPlayerName.Value.Substring(0, Mathf.Min(ClientGameLoop.clientPlayerName.Value.Length, 16));
m_requestedPlayerSettings.playerName = ClientGameLoop.clientPlayerName.Value;
m_playerSettingsUpdated = true;
}
if (IsConnected() && m_playerSettingsUpdated)
{
m_playerSettingsUpdated = false;
SendPlayerSettings();
}
if (HasSingleton<NetworkSnapshotAckComponent>())
{
if (Game.game.m_GameStatistics != null)
{
Game.game.m_GameStatistics.rtt = Mathf.RoundToInt(GetSingleton<NetworkSnapshotAckComponent>().EstimatedRTT);
Game.game.m_GameStatistics.commandAge = GetSingleton<NetworkSnapshotAckComponent>().ServerCommandAge / 256.0f;
}
}
}
void EnterBrowsingState()
{
GameDebug.Assert(m_clientWorld == null);
m_ClientState = ClientState.Browsing;
}
void UpdateBrowsingState()
{
}
void LeaveBrowsingState()
{
}
string targetServer = "";
int connectRetryCount;
void EnterConnectingState()
{
GameDebug.Assert(m_ClientState == ClientState.Browsing, "Expected ClientState to be browsing");
GameDebug.Assert(m_clientWorld == null, "Expected ClientWorld to be null");
GameDebug.Assert(!IsConnected(), "Expected network connectionState to be disconnected");
m_ClientState = ClientState.Connecting;
connectRetryCount = 0;
}
void UpdateConnectingState()
{
if (m_ConnectionQuery.CalculateEntityCount() == 0)
{
if (connectRetryCount < 2)
{
++connectRetryCount;
var gameMessage = string.Format("Trying to connect to {0} (attempt #{1})...", targetServer, connectRetryCount);
GameDebug.Log(gameMessage);
NetworkEndPoint ep;
if (targetServer == "localhost")
{
ep = NetworkEndPoint.LoopbackIpv4;
ep.Port = NetworkConfig.defaultServerPort;
}
else
ep = NetworkEndPoint.Parse(targetServer, NetworkConfig.defaultServerPort);
m_NetworkStreamReceiveSystem.Connect(ep);
}
else
{
var gameMessage = "Failed to connect to server";
GameDebug.Log(gameMessage);
m_StateMachine.SwitchTo(m_GameWorld,ClientState.Browsing);
}
}
else if (IsConnected())
{
m_GameMessage = "Waiting for map info";
}
var entityManager = m_GameWorld.EntityManager;
var query = entityManager
.CreateEntityQuery(ComponentType.ReadWrite<ActiveStateComponentData>());
if (query.CalculateEntityCount() == 1)
{
var entity = query.GetSingletonEntity();
if (!IsConnected())
{
EntityManager.DestroyEntity(entity);
return;
}
var mapInfo = entityManager.GetComponentData<ActiveStateComponentData>(entity);
m_LevelName = mapInfo.MapName.ToString();
if (m_StateMachine.CurrentState() != ClientState.Loading)
m_StateMachine.SwitchTo(m_GameWorld,ClientState.Loading);
entityManager.DestroyEntity(entity);
}
}
void EnterLoadingState()
{
if (Game.game.clientFrontend != null)
Game.game.clientFrontend.ShowMenu(ClientFrontend.MenuShowing.None);
Console.SetOpen(false);
GameDebug.Assert(m_clientWorld == null);
GameDebug.Assert(IsConnected());
m_requestedPlayerSettings.playerName = ClientGameLoop.clientPlayerName.Value;
m_requestedPlayerSettings.characterType = (short)Game.characterType.IntValue;
m_playerSettingsUpdated = true;
m_ClientState = ClientState.Loading;
}
void UpdateLoadingState()
{
// Handle disconnects
if (!IsConnected())
{
m_GameMessage = m_DisconnectReason != null ? string.Format("Disconnected from server ({0})", m_DisconnectReason) : "Disconnected from server (lost connection)";
m_DisconnectReason = null;
m_StateMachine.SwitchTo(m_GameWorld,ClientState.Browsing);
}
// Wait until we got level info
if (m_LevelName == null)
return;
// Load if we are not already loading
var level = Game.game.levelManager.currentLevel;
if (level == null || level.name != m_LevelName)
{
if (!Game.game.levelManager.LoadLevel(m_LevelName))
{
m_DisconnectReason = string.Format("could not load requested level '{0}'", m_LevelName);
Disconnect();
return;
}
level = Game.game.levelManager.currentLevel;
}
// Wait for level to be loaded
if (Game.game.levelManager.IsCurrentLevelLoaded())
m_StateMachine.SwitchTo(m_GameWorld,ClientState.WaitSubscene);
}
void UpdateWaitSubsceneState()
{
// TODO (mogensh) we should find a better way to make sure subscene is loaded (this uses knowledge of what is in subscene)
var query = m_GameWorld.EntityManager.CreateEntityQuery(typeof(HeroRegistry.RegistryEntity));
var ready = query.CalculateEntityCount() > 0;
query.Dispose();
if(ready)
m_StateMachine.SwitchTo(m_GameWorld,ClientState.Playing);
}
void EnterPlayingState()
{
GameDebug.Assert(m_clientWorld == null && Game.game.levelManager.IsCurrentLevelLoaded());
//if (Game.netUseDotsNetworking.IntValue > 0 && netCodePrefabs == Entity.Null)
// netCodePrefabs = PrefabAssetManager.CreateEntity(m_GameWorld.GetECSWorld(), Game.game.dotsNetCodePrefabs);
var idQuery = m_NetworkStreamReceiveSystem.EntityManager
.CreateEntityQuery(ComponentType.ReadWrite<NetworkIdComponent>(), ComponentType.Exclude<NetworkStreamDisconnected>());
var clientId = m_NetworkStreamReceiveSystem.EntityManager.GetComponentData<NetworkIdComponent>(idQuery.GetSingletonEntity()).Value;
m_clientWorld = new ClientGameWorld(m_GameWorld, clientId);
m_LocalPlayer = m_clientWorld.RegisterLocalPlayer(clientId);
var eventSystem = m_GameWorld.GetOrCreateSystem<ClientReadyEventSystem>();
eventSystem.SendPlayerReadyRpc();
m_ClientState = ClientState.Playing;
m_GameWorld.GetExistingSystem<BeforeClientPredictionSystem>().GameWorld = m_clientWorld;
m_GameWorld.GetExistingSystem<ClientPredictionSystem>().GameWorld = m_clientWorld;
m_GameWorld.GetExistingSystem<AfterClientPredictionSystem>().GameWorld = m_clientWorld;
var lateUpdate = m_GameWorld.GetExistingSystem<ClientLateUpdateSystem>();
lateUpdate.GameWorld = m_clientWorld;
}
void LeavePlayingState()
{
if (!m_isDestroyingWorld)
{
if (IsConnected())
Disconnect();
m_GameWorld.GetExistingSystem<BeforeClientPredictionSystem>().GameWorld = null;
m_GameWorld.GetExistingSystem<ClientPredictionSystem>().GameWorld = null;
m_GameWorld.GetExistingSystem<AfterClientPredictionSystem>().GameWorld = null;
m_GameWorld.GetExistingSystem<ClientLateUpdateSystem>().GameWorld = null;
}
m_LocalPlayer = Entity.Null;
m_clientWorld.Shutdown(m_isDestroyingWorld);
m_clientWorld = null;
// TODO (petera) replace this with a stack of levels or similar thing. For now we just load the menu no matter what
//Game.game.levelManager.UnloadLevel();
//Game.game.levelManager.LoadLevel("level_menu");
PrefabAssetManager.Shutdown();
if (!m_isDestroyingWorld)
GameLoop.RequestNewWorld();
// Game can be null because destruction order when exiting playmode is undefined right now
if (Game.game != null && Game.game.clientFrontend != null)
{
Game.game.clientFrontend.Clear();
Game.game.clientFrontend.ShowMenu(ClientFrontend.MenuShowing.None);
}
#if UNITY_EDITOR
// Game can be null because destruction order when exiting playmode is undefined right now
if (Game.game != null && !GameBootStrap.IsSingleLevelPlaymode)
#endif
Game.game.levelManager.LoadLevel("level_menu");
GameDebug.Log("Left playingstate");
}
void UpdatePlayingState()
{
// Handle disconnects
if (!IsConnected())
{
m_GameMessage = m_DisconnectReason != null ? string.Format("Disconnected from server ({0})", m_DisconnectReason) : "Disconnected from server (lost connection)";
m_StateMachine.SwitchTo(m_GameWorld,ClientState.Browsing);
return;
}
if (GatedInput.GetKeyUp(KeyCode.H))
{
RemoteConsoleCommand("nextchar");
}
if (GatedInput.GetKeyUp(KeyCode.T))
CmdNextTeam(null);
float frameDuration = m_lastFrameTime != 0 ? (float)(Game.frameTime - m_lastFrameTime) : 0;
m_lastFrameTime = Game.frameTime;
}
public void RemoteConsoleCommand(string command)
{
m_GameWorld.GetOrCreateSystem<ClientEventSystem>().SendRemoteCommandRpc(new NativeString64(command));
}
public void CmdConnect(string[] args)
{
if (m_StateMachine.CurrentState() == ClientState.Browsing)
{
targetServer = args.Length > 0 ? args[0] : "127.0.0.1";
m_StateMachine.SwitchTo(m_GameWorld,ClientState.Connecting);
}
else if (m_StateMachine.CurrentState() == ClientState.Connecting)
{
Disconnect();
targetServer = args.Length > 0 ? args[0] : "127.0.0.1";
connectRetryCount = 0;
}
else
{
GameDebug.Log("Unable to connect from this state: " + m_StateMachine.CurrentState().ToString());
}
}
void CmdDisconnect(string[] args)
{
m_DisconnectReason = "user manually disconnected";
Disconnect();
m_StateMachine.SwitchTo(m_GameWorld,ClientState.Browsing);
}
void CmdRunAtServer(string[] args)
{
RemoteConsoleCommand(string.Join(" ", args));
}
void CmdRespawn(string[] args)
{
if (m_LocalPlayer == Entity.Null)
return;
var localPlayerState = m_GameWorld.EntityManager.GetComponentData<LocalPlayer>(m_LocalPlayer);
if (localPlayerState.playerEntity == Entity.Null)
return;
var playerState = m_GameWorld.EntityManager.GetComponentData<Player.State>(localPlayerState.playerEntity);
if (playerState.controlledEntity == Entity.Null)
return;
// Request new char type
if (args.Length == 1)
{
m_requestedPlayerSettings.characterType = short.Parse(args[0]);
m_playerSettingsUpdated = true;
}
// Tell server who to respawn
RemoteConsoleCommand(string.Format("s_respawn {0}", playerState.playerId));
}
void CmdNextChar(string[] args)
{
if (m_LocalPlayer == Entity.Null)
return;
var localPlayerState = m_GameWorld.EntityManager.GetComponentData<LocalPlayer>(m_LocalPlayer);
if (localPlayerState.playerEntity == Entity.Null)
return;
var playerState = m_GameWorld.EntityManager.GetComponentData<Player.State>(localPlayerState.playerEntity);
if (playerState.controlledEntity == Entity.Null)
return;
if (Game.allowCharChange.IntValue != 1)
return;
if (!m_GameWorld.EntityManager.HasComponent<Character.State>(playerState.controlledEntity))
return;
var heroRegistry = HeroRegistry.GetRegistry(m_GameWorld.EntityManager);
var charSetupCount = heroRegistry.Value.Heroes.Length;
m_requestedPlayerSettings.characterType = m_requestedPlayerSettings.characterType + 1;
if (m_requestedPlayerSettings.characterType >= charSetupCount)
m_requestedPlayerSettings.characterType = 0;
m_playerSettingsUpdated = true;
}
void CmdNextTeam(string[] args)
{
if (m_LocalPlayer == Entity.Null)
return;
var localPlayerState = m_GameWorld.EntityManager.GetComponentData<LocalPlayer>(m_LocalPlayer);
if (localPlayerState.playerEntity == Entity.Null)
return;
if (Game.allowCharChange.IntValue != 1)
return;
var playerState = m_GameWorld.EntityManager.GetComponentData<Player.State>(localPlayerState.playerEntity);
m_requestedPlayerSettings.teamId = (short)(playerState.teamIndex + 1);
if (m_requestedPlayerSettings.teamId > 1)
m_requestedPlayerSettings.teamId = 0;
m_playerSettingsUpdated = true;
}
void SendPlayerSettings()
{
var eventSystem = m_GameWorld.GetOrCreateSystem<ClientEventSystem>();
eventSystem.SendPlayerSettingsRPC(m_requestedPlayerSettings);
}
enum ClientState
{
Browsing,
Connecting,
Loading,
WaitSubscene,
Playing,
}
StateMachine<ClientState> m_StateMachine;
ClientState m_ClientState;
World m_GameWorld;
Entity m_LocalPlayer;
PlayerSettings m_requestedPlayerSettings = new PlayerSettings();
bool m_playerSettingsUpdated;
ClientGameWorld m_clientWorld;
string m_LevelName;
string m_DisconnectReason = null;
string m_GameMessage = "Welcome to the sample game!";
double m_lastFrameTime;
private bool m_isDestroyingWorld;
[ConfigVar(Name = "client.showtickinfo", DefaultValue = "0", Description = "Show tick info")]
static ConfigVar m_showTickInfo;
}