您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

931 行
32 KiB

//#define USE_UNET
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using NetworkCompression;
using UnityEngine.Profiling;
using SQP;
public class ServerGameWorld : ISnapshotGenerator, IClientCommandProcessor
{
public int WorldTick { get { return m_GameWorld.worldTime.tick; } }
public int TickRate
{
get
{
return m_GameWorld.worldTime.tickRate;
}
set
{
m_GameWorld.worldTime.tickRate = value;
}
}
public float TickInterval { get { return m_GameWorld.worldTime.tickInterval; } }
public ServerGameWorld(GameWorld world, NetworkServer networkServer, Dictionary<int, ServerGameLoop.ClientInfo> clients, ChatSystemServer m_ChatSystem, BundledResourceManager resourceSystem)
{
m_NetworkServer = networkServer;
m_Clients = clients;
this.m_ChatSystem = m_ChatSystem;
m_GameWorld = world;
m_CharacterModule = new CharacterModuleServer(m_GameWorld, resourceSystem);
m_ProjectileModule = new ProjectileModuleServer(m_GameWorld, resourceSystem);
m_HitCollisionModule = new HitCollisionModule(m_GameWorld, 128, 1);
m_PlayerModule = new PlayerModuleServer(m_GameWorld, resourceSystem);
m_DebugPrimitiveModule = new DebugPrimitiveModule(m_GameWorld, 0.4f, 0.02f);
m_SpectatorCamModule = new SpectatorCamModuleServer(m_GameWorld, resourceSystem);
m_ReplicatedEntityModule = new ReplicatedEntityModuleServer(m_GameWorld, resourceSystem, m_NetworkServer);
m_ReplicatedEntityModule.ReserveSceneEntities(networkServer);
m_GameModeSystem = m_GameWorld.GetECSWorld().CreateManager<GameModeSystemServer>(m_GameWorld, m_ChatSystem, resourceSystem);
m_DestructablePropSystem = m_GameWorld.GetECSWorld().CreateManager<UpdateDestructableProps>(m_GameWorld);
m_DamageAreaSystem = m_GameWorld.GetECSWorld().CreateManager<DamageAreaSystemServer>(m_GameWorld);
m_TeleporterSystem = m_GameWorld.GetECSWorld().CreateManager<TeleporterSystemServer>(m_GameWorld);
m_HandleGrenadeRequests = m_GameWorld.GetECSWorld().CreateManager<HandleGrenadeRequest>(m_GameWorld, resourceSystem);
m_StartGrenadeMovement = m_GameWorld.GetECSWorld().CreateManager<StartGrenadeMovement>(m_GameWorld);
m_FinalizeGrenadeMovement = m_GameWorld.GetECSWorld().CreateManager<FinalizeGrenadeMovement>(m_GameWorld);
m_platformSystem = m_GameWorld.GetECSWorld().CreateManager<MoverUpdate>(m_GameWorld);
m_MoveableSystem = new MovableSystemServer(m_GameWorld, resourceSystem);
m_CameraSystem = new ServerCameraSystem(m_GameWorld);
}
public void Shutdown()
{
m_CharacterModule.Shutdown();
m_ProjectileModule.Shutdown();
m_HitCollisionModule.Shutdown();
m_PlayerModule.Shutdown();
m_DebugPrimitiveModule.Shutdown();
m_SpectatorCamModule.Shutdown();
m_GameWorld.GetECSWorld().DestroyManager(m_DestructablePropSystem);
m_GameWorld.GetECSWorld().DestroyManager(m_DamageAreaSystem);
m_GameWorld.GetECSWorld().DestroyManager(m_TeleporterSystem);
m_GameWorld.GetECSWorld().DestroyManager(m_HandleGrenadeRequests);
m_GameWorld.GetECSWorld().DestroyManager(m_StartGrenadeMovement);
m_GameWorld.GetECSWorld().DestroyManager(m_FinalizeGrenadeMovement);
m_GameWorld.GetECSWorld().DestroyManager(m_platformSystem);
m_ReplicatedEntityModule.Shutdown();
m_CameraSystem.Shutdown();
m_MoveableSystem.Shutdown();
m_GameWorld = null;
}
public void RespawnPlayer(PlayerState player)
{
if(player.controlledEntity == Entity.Null)
return;
if (m_GameWorld.GetEntityManager().HasComponent<CharacterPredictedState>(player.controlledEntity))
CharacterDespawnRequest.Create(m_GameWorld,player.controlledEntity);
player.controlledEntity = Entity.Null;
}
char[] _msgBuf = new char[256];
public void HandlePlayerSetupEvent(PlayerState player, PlayerSettings settings)
{
if (player.playerName != settings.playerName)
{
int l = 0;
if (player.playerName == "")
l = StringFormatter.Write(ref _msgBuf, 0, "{0} joined", settings.playerName);
else
l = StringFormatter.Write(ref _msgBuf, 0, "{0} is now known as {1}", player.playerName, settings.playerName);
m_ChatSystem.SendChatAnnouncement(new CharBufView(_msgBuf, l));
player.playerName = settings.playerName;
}
var playerEntity = player.gameObject.GetComponent<GameObjectEntity>().Entity;
var charControl = m_GameWorld.GetEntityManager().GetComponentObject<PlayerCharacterControl>(playerEntity);
charControl.requestedCharacterType = settings.characterType;
}
public void ProcessCommand(int connectionId, int tick, ref NetworkReader data)
{
ServerGameLoop.ClientInfo client;
if (!m_Clients.TryGetValue(connectionId, out client))
return;
if(client.player)
{
if (tick == m_GameWorld.worldTime.tick)
client.latestCommand.Deserialize(ref data, null, tick);
// Pass on command to controlled entity
if (client.player.controlledEntity != Entity.Null)
{
var userCommand = m_GameWorld.GetEntityManager().GetComponentObject<UserCommandComponent>(client.player.controlledEntity);
GameDebug.Assert(userCommand != null, "Controlled entity does not have UserCommandComponent");
userCommand.prevCommand = userCommand.command;
userCommand.command = client.latestCommand;
}
}
}
public bool HandleClientCommand(ServerGameLoop.ClientInfo client, string v)
{
if(v == "nextchar")
{
GameDebug.Log("nextchar for client " + client.id);
m_GameModeSystem.RequestNextChar(client.player);
}
else
{
return false;
}
return true;
}
public void ServerTickUpdate()
{
Profiler.BeginSample("ServerGameWorld.ServerTickUpdate()");
m_GameWorld.worldTime.tick++;
m_GameWorld.worldTime.tickDuration = m_GameWorld.worldTime.tickInterval;
m_GameWorld.frameDuration = m_GameWorld.worldTime.tickInterval;
Profiler.BeginSample("HandleClientCommands");
// This call backs into ProcessCommand
m_NetworkServer.HandleClientCommands(m_GameWorld.worldTime.tick, this);
Profiler.EndSample();
GameTime gameTime = new GameTime(m_GameWorld.worldTime.tickRate);
gameTime.SetTime(m_GameWorld.worldTime.tick, m_GameWorld.worldTime.tickInterval);
// Handle spawn requests. All creation of game entities should happen in this phase
m_CharacterModule.HandleSpawnRequests();
m_SpectatorCamModule.HandleSpawnRequests();
m_ProjectileModule.HandleRequests();
m_DebugPrimitiveModule.HandleRequests();
m_HandleGrenadeRequests.Update();
// Handle controlled entity changed
m_CharacterModule.HandleControlledEntityChanged();
// Handle newly spawned entities
m_CharacterModule.HandleSpawns();
m_HitCollisionModule.HandleSpawning();
m_ReplicatedEntityModule.HandleSpawning();
// Start movement of scene objects. Scene objects that player movement
// depends on should finish movement in this phase
m_MoveableSystem.Update();
m_platformSystem.Update();
m_ProjectileModule.MovementStart();
m_StartGrenadeMovement.Update();
m_CameraSystem.Update();
// Update movement of player controlled units
m_TeleporterSystem.Update();
m_CharacterModule.MovementStart();
m_CharacterModule.MovementResolve();
// Update abilities
m_CharacterModule.AbilityStart();
m_CharacterModule.AbilityResolve();
// Finalize movement of modules that only depend on data from previous frames
// We want to wait as long as possible so queries potentially can be handled in jobs
m_ProjectileModule.MovementResolve();
m_FinalizeGrenadeMovement.Update();
// Handle damage
m_DestructablePropSystem.Update();
m_DamageAreaSystem.Update();
m_HitCollisionModule.HandleSplashDamage();
m_CharacterModule.HandleDamage();
//
m_CharacterModule.PresentationUpdate();
// Update gamemode. Run last to allow picking up deaths etc.
m_GameModeSystem.Update();
// Handle despawns
m_HitCollisionModule.HandleDespawn();
m_CharacterModule.HandleDepawns();
m_ReplicatedEntityModule.HandleDespawning();
m_GameWorld.ProcessDespawns();
Profiler.EndSample();
}
// This is called every render frame where an tick update has been performed
public void LateUpdate()
{
m_CharacterModule.AttachmentUpdate();
m_HitCollisionModule.StoreColliderState();
#if UNITY_EDITOR
m_DebugPrimitiveModule.DrawPrimitives();
#endif
}
public void HandleClientConnect(ServerGameLoop.ClientInfo client)
{
client.player = m_PlayerModule.CreatePlayer(m_GameWorld, client.id, "", client.isReady);
}
public void HandleClientDisconnect(ServerGameLoop.ClientInfo client)
{
m_PlayerModule.CleanupPlayer(client.player);
m_CharacterModule.CleanupPlayer(client.player);
}
public void GenerateEntitySnapshot(int entityId, ref NetworkWriter writer)
{
Profiler.BeginSample("ServerGameLoop.GenerateEntitySnapshot()");
m_ReplicatedEntityModule.GenerateEntitySnapshot(entityId, ref writer);
Profiler.EndSample();
}
public string GenerateEntityName(int entityId)
{
return m_ReplicatedEntityModule.GenerateName(entityId);
}
// External systems
NetworkServer m_NetworkServer;
Dictionary<int, ServerGameLoop.ClientInfo> m_Clients;
readonly ChatSystemServer m_ChatSystem;
// Internal systems
GameWorld m_GameWorld;
readonly CharacterModuleServer m_CharacterModule;
readonly ProjectileModuleServer m_ProjectileModule;
readonly HitCollisionModule m_HitCollisionModule;
readonly PlayerModuleServer m_PlayerModule;
readonly DebugPrimitiveModule m_DebugPrimitiveModule;
readonly SpectatorCamModuleServer m_SpectatorCamModule;
readonly ReplicatedEntityModuleServer m_ReplicatedEntityModule;
readonly ServerCameraSystem m_CameraSystem;
readonly GameModeSystemServer m_GameModeSystem;
readonly DamageAreaSystemServer m_DamageAreaSystem;
readonly TeleporterSystemServer m_TeleporterSystem;
readonly HandleGrenadeRequest m_HandleGrenadeRequests;
readonly StartGrenadeMovement m_StartGrenadeMovement;
readonly FinalizeGrenadeMovement m_FinalizeGrenadeMovement;
readonly MoverUpdate m_platformSystem;
readonly UpdateDestructableProps m_DestructablePropSystem;
readonly MovableSystemServer m_MoveableSystem;
}
public class ServerGameLoop : Game.IGameLoop, INetworkCallbacks
{
public bool Init(string[] args)
{
// Set up statemachine for ServerGame
m_StateMachine = new StateMachine<ServerState>();
m_StateMachine.Add(ServerState.Idle, null, UpdateIdleState, null);
m_StateMachine.Add(ServerState.Loading, null, UpdateLoadingState, null);
m_StateMachine.Add(ServerState.Active, EnterActiveState, UpdateActiveState, LeaveActiveState);
m_StateMachine.SwitchTo(ServerState.Idle);
#if USE_UNET
m_NetworkTransport = new UNETTransport();
if (!m_NetworkTransport.Init(serverPort.IntValue, 32))
return false;
m_NetworkServer = new NetworkServer(m_NetworkTransport);
m_ServerBroadcast = new UNETServerBroadcast(m_NetworkTransport.hostId, new UNETBroadcastConfig(), 7913);
// TODO (petera) what if these (max clients and name) changes during play
m_ServerBroadcast.gameInfo.maxPlayers = serverMaxClients.IntValue;
if (serverServerName.Value.Length == 0)
serverServerName.Value = MakeServername();
m_ServerBroadcast.gameInfo.servername = serverServerName.Value;
GameDebug.Log("Server name: '" + m_ServerBroadcast.gameInfo.servername + "'");
m_ServerBroadcast.gameInfo.levelname = "-";
m_ServerBroadcast.gameInfo.gamemode = "-";
m_ServerBroadcast.Start();
#else
m_NetworkTransport = new SocketTransport(serverPort.IntValue);
GameDebug.Log("Listening on " + string.Join(", ", NetworkUtils.GetLocalInterfaceAddresses()) + " on port " + serverPort.IntValue);
m_NetworkServer = new NetworkServer(m_NetworkTransport);
if(Game.game.clientFrontend != null)
{
var serverPanel = Game.game.clientFrontend.serverPanel;
serverPanel.SetPanelActive(true);
serverPanel.serverInfo.text += "Listening on:\n";
foreach(var a in NetworkUtils.GetLocalInterfaceAddresses())
{
serverPanel.serverInfo.text += a + ":" + serverPort.IntValue + "\n";
}
}
#endif
m_NetworkServer.UpdateClientInfo();
m_NetworkServer.clientInfo.compressionModel = m_Model;
m_ServerQueryProtocolServer = new SQP.SQPServer(serverSQPPort.IntValue);
var info = m_ServerQueryProtocolServer.ServerInfoData;
info.BuildId = Game.game.buildId;
info.Port = (ushort)serverPort.IntValue;
info.CurrentPlayers = 0;
info.GameType = "Deathmatch";
info.Map = "";
info.MaxPlayers = 32;
info.ServerName = MakeServername();
#if UNITY_EDITOR
Game.game.levelManager.UnloadLevel();
World.DisposeAllWorlds();
#endif
m_GameWorld = new GameWorld("ServerWorld");
m_NetworkStatistics = new NetworkStatisticsServer(m_NetworkServer);
m_ChatSystem = new ChatSystemServer(m_Clients, m_NetworkServer);
GameDebug.Log("Network server initialized");
Console.AddCommand("load", CmdLoad, "Load a named scene", this.GetHashCode());
Console.AddCommand("unload", CmdUnload, "Unload current scene", this.GetHashCode());
Console.AddCommand("respawn", CmdRespawn, "Respawn character (usage : respawn playername|playerId)", this.GetHashCode());
Console.AddCommand("servername", CmdSetServerName, "Set name of the server", this.GetHashCode());
Console.AddCommand("beginnetworkprofile", CmdBeginNetworkProfile, "begins a network profile", this.GetHashCode());
Console.AddCommand("endnetworkprofile", CmdEndNetworkProfile, "Ends a network profile and analyzes. [optional] filepath for model data", this.GetHashCode());
Console.AddCommand("loadcompressionmodel", CmdLoadNetworkCompressionModel, "Loads a network compression model from a filepath", this.GetHashCode());
Console.AddCommand("list", CmdList, "List clients", this.GetHashCode());
CmdLoad(args);
Game.SetMousePointerLock(false);
m_ServerStartTime = Time.time;
GameDebug.Log("Server initialized");
Console.SetOpen(false);
return true;
}
public void Shutdown()
{
GameDebug.Log("ServerGameState shutdown");
Console.RemoveCommandsWithTag(this.GetHashCode());
m_StateMachine.Shutdown();
m_NetworkServer.Shutdown();
#if USE_UNET
m_ServerBroadcast.Stop();
#endif
m_NetworkTransport.Shutdown();
Game.game.levelManager.UnloadLevel();
m_GameWorld.Shutdown();
m_GameWorld = null;
}
public void Update()
{
if(serverRecycleInterval.FloatValue > 0.0f)
{
// Recycle server if time is up and no clients connected
if(m_Clients.Count == 0 && Time.time > m_ServerStartTime + serverRecycleInterval.FloatValue)
{
GameDebug.Log("Server exiting because recycle timeout was hit.");
Console.EnqueueCommandNoHistory("quit");
}
}
if (m_Clients.Count > m_MaxClients)
m_MaxClients = m_Clients.Count;
if(serverQuitWhenEmpty.IntValue > 0 && m_MaxClients > 0 && m_Clients.Count == 0)
{
GameDebug.Log("Server exiting because last client disconnected");
Console.EnqueueCommandNoHistory("quit");
}
m_SimStartTime = Game.Clock.ElapsedTicks;
m_SimStartTimeTick = m_serverGameWorld != null ? m_serverGameWorld.WorldTick : 0;
UpdateNetwork();
m_StateMachine.Update();
m_NetworkServer.SendData();
m_NetworkStatistics.Update();
if (showGameLoopInfo.IntValue > 0)
OnDebugDrawGameloopInfo();
}
public NetworkServer GetNetworkServer()
{
return m_NetworkServer;
}
public void OnConnect(int id)
{
var client = new ClientInfo();
client.id = id;
m_Clients.Add(id, client);
if (m_serverGameWorld != null)
m_serverGameWorld.HandleClientConnect(client);
m_ServerQueryProtocolServer.ServerInfoData.CurrentPlayers = (ushort)m_Clients.Count;
}
public void OnDisconnect(int id)
{
ClientInfo client;
if (m_Clients.TryGetValue(id, out client))
{
if (m_serverGameWorld != null)
m_serverGameWorld.HandleClientDisconnect(client);
m_Clients.Remove(id);
}
m_ServerQueryProtocolServer.ServerInfoData.CurrentPlayers = (ushort)m_Clients.Count;
}
public void OnEvent(int clientId, NetworkEvent info)
{
var client = m_Clients[clientId];
var type = info.type.typeId;
var data = new NetworkReader(info.data, info.type.schema);
switch ((GameNetworkEvents.EventType)type)
{
case GameNetworkEvents.EventType.PlayerReady:
m_NetworkServer.MapReady(clientId); // TODO (petera) hacky
client.isReady = true;
break;
case GameNetworkEvents.EventType.PlayerSetup:
client.playerSettings.Deserialize(ref data);
if (client.player != null)
m_serverGameWorld.HandlePlayerSetupEvent(client.player, client.playerSettings);
break;
case GameNetworkEvents.EventType.RemoteConsoleCmd:
HandleClientCommand(client, data.ReadString());
break;
case GameNetworkEvents.EventType.Chat:
m_ChatSystem.ReceiveMessage(client, data.ReadString(256));
break;
}
}
private void HandleClientCommand(ClientInfo client, string v)
{
if (m_serverGameWorld != null && m_serverGameWorld.HandleClientCommand(client, v))
return;
// Fall back is just to become a server console command
// TODO (petera) Add some sort of security system here
Console.EnqueueCommandNoHistory(v);
}
void UpdateNetwork()
{
Profiler.BeginSample("ServerGameLoop.UpdateNetwork");
// If serverTickrate was changed, update both game world and
if ((ConfigVar.DirtyFlags & ConfigVar.Flags.ServerInfo) == ConfigVar.Flags.ServerInfo)
{
GameDebug.Log("WARNING: UpdateClientInfo deprecated");
m_NetworkServer.UpdateClientInfo();
ConfigVar.DirtyFlags &= ~ConfigVar.Flags.ServerInfo;
}
if (m_serverGameWorld != null && m_serverGameWorld.TickRate != Game.serverTickRate.IntValue)
m_serverGameWorld.TickRate = Game.serverTickRate.IntValue;
#if USE_UNET
m_ServerBroadcast.UpdateBroadcast();
#endif
m_ServerQueryProtocolServer.Update();
m_NetworkServer.Update(this);
Profiler.EndSample();
}
/// <summary>
/// Idle state, no level is loaded
/// </summary>
void UpdateIdleState()
{
}
/// <summary>
/// Loading state, load in progress
/// </summary>
void UpdateLoadingState()
{
if (Game.game.levelManager.IsCurrentLevelLoaded())
m_StateMachine.SwitchTo(ServerState.Active);
}
/// <summary>
/// Active state, level loaded
/// </summary>
void EnterActiveState()
{
GameDebug.Assert(m_serverGameWorld == null);
m_GameWorld.RegisterSceneEntities();
m_ServerQueryProtocolServer.ServerInfoData.Map = Game.game.levelManager.currentLevel.name;
m_resourceSystem = new BundledResourceManager("BundledResources/Server");
m_NetworkServer.InitializeMap((ref NetworkWriter data) =>
{
data.WriteString("name", Game.game.levelManager.currentLevel.name);
});
m_serverGameWorld = new ServerGameWorld(m_GameWorld, m_NetworkServer, m_Clients, m_ChatSystem, m_resourceSystem);
foreach (var pair in m_Clients)
{
m_serverGameWorld.HandleClientConnect(pair.Value);
}
#if USE_UNET
//m_ServerBroadcast.gameInfo.levelname = Game.game.levelManager.currentLevel.name;
//m_ServerBroadcast.gameInfo.gamemode = "Deathmatch";
//m_ServerBroadcast.UpdateGameInfo();
#endif
}
Dictionary<int, int> m_TickStats = new Dictionary<int, int>();
void UpdateActiveState()
{
GameDebug.Assert(m_serverGameWorld != null);
int tickCount = 0;
while (Game.frameTime > m_nextTickTime)
{
tickCount++;
m_serverGameWorld.ServerTickUpdate();
Profiler.BeginSample("GenerateSnapshots");
m_NetworkServer.GenerateSnapshot(m_serverGameWorld, m_LastSimTime);
Profiler.EndSample();
m_nextTickTime += m_serverGameWorld.TickInterval;
m_performLateUpdate = true;
}
//
// If running as headless we nudge the Application.targetFramerate back and forth
// around the actual framerate -- always trying to have a remaining time of half a frame
// The goal is to have the while loop above tick exactly 1 time
//
// The reason for using targetFramerate is to allow Unity to sleep between frames
// reducing cpu usage on server.
//
if (Game.IsHeadless())
{
float remainTime = (float)(m_nextTickTime - Game.frameTime);
int rate = m_serverGameWorld.TickRate;
if (remainTime > 0.75f * m_serverGameWorld.TickInterval)
rate -= 2;
else if (remainTime < 0.25f * m_serverGameWorld.TickInterval)
rate += 2;
Application.targetFrameRate = rate;
//
// Show some stats about how many world ticks per unity update we have been running
//
if (debugServerTickStats.IntValue > 0)
{
if (Time.frameCount % 10 == 0)
GameDebug.Log(remainTime + ":" + rate);
if (!m_TickStats.ContainsKey(tickCount))
m_TickStats[tickCount] = 0;
m_TickStats[tickCount] = m_TickStats[tickCount] + 1;
if (Time.frameCount % 100 == 0)
{
foreach (var p in m_TickStats)
{
GameDebug.Log(p.Key + ":" + p.Value);
}
}
}
}
}
void LeaveActiveState()
{
m_serverGameWorld.Shutdown();
m_serverGameWorld = null;
m_resourceSystem.Shutdown();
}
public void FixedUpdate()
{
}
public void LateUpdate()
{
if (m_serverGameWorld != null && m_SimStartTimeTick != m_serverGameWorld.WorldTick)
{
// Only update sim time if we actually simulatated
// TODO : remove this when targetFrameRate works the way we want it.
m_LastSimTime = Game.Clock.GetTicksDeltaAsMilliseconds(m_SimStartTime);
}
if (m_performLateUpdate)
{
m_serverGameWorld.LateUpdate();
m_performLateUpdate = false;
}
}
void LoadLevel(string levelname, string gamemode = "deathmatch")
{
if (!Game.game.levelManager.CanLoadLevel(levelname))
{
GameDebug.Log("ERROR : Cannot load level : " + levelname);
return;
}
m_RequestedGameMode = gamemode;
Game.game.levelManager.LoadLevel(levelname);
m_StateMachine.SwitchTo(ServerState.Loading);
}
void UnloadLevel()
{
// TODO
}
void CmdSetServerName(string[] args)
{
if (args.Length > 0)
{
#if USE_UNET
m_ServerBroadcast.gameInfo.servername = args[0];
m_ServerBroadcast.UpdateGameInfo();
GameDebug.Log("Server name : '" + m_ServerBroadcast.gameInfo.servername + "'");
#endif
}
else
Console.Write("Invalid argument to servername (usage : servername name)");
}
void CmdLoad(string[] args)
{
if (args.Length == 1)
LoadLevel(args[0]);
else if (args.Length == 2)
LoadLevel(args[0], args[1]);
}
void CmdUnload(string[] args)
{
UnloadLevel();
}
void CmdRespawn(string[] args)
{
if (args.Length != 1)
{
Console.Write("Invalid argument for respawn command (usage : respawn playername|playerId)");
return;
}
var playerId = -1;
var playerName = args[0];
var usePlayerId = int.TryParse(args[0], out playerId);
foreach (var pair in m_Clients)
{
var client = pair.Value;
if (client.player == null)
continue;
if (usePlayerId && client.id != playerId)
continue;
if (!usePlayerId && client.player.playerName != playerName)
continue;
m_serverGameWorld.RespawnPlayer(client.player);
}
Console.Write("Could not find character. Unknown player, invalid character id or player doesn't have a character" + args[0]);
}
void CmdBeginNetworkProfile(string[] args)
{
var networkServer = GetNetworkServer();
if (networkServer != null)
{
networkServer.StartNetworkProfile();
Console.Write("Profiling started");
}
else
{
Console.Write("No server running");
}
}
void CmdEndNetworkProfile(string[] args)
{
var networkServer = GetNetworkServer();
if (networkServer != null)
networkServer.EndNetworkProfile(args.Length >= 1 ? args[0] : null);
else
Console.Write("No server running");
}
void CmdLoadNetworkCompressionModel(string[] args)
{
var networkServer = GetNetworkServer();
if (networkServer != null && networkServer.GetConnections().Count > 0)
{
Console.Write("Can only load compression model when server when no clients are connected");
return;
}
if (args.Length != 1)
{
Console.Write("Syntax: loadcompressionmodel filepath");
return;
}
byte[] modelData = null;
try
{
modelData = System.IO.File.ReadAllBytes(args[0]);
}
catch (System.Exception e)
{
Console.Write("Failed to read file: " + args[0] + " ("+e.ToString()+")");
return;
}
m_Model = new NetworkCompressionModel(modelData);
if (networkServer != null)
networkServer.clientInfo.compressionModel = m_Model;
Console.Write("Model Loaded");
}
void CmdList(string[] args)
{
Console.Write("Players on server:");
Console.Write("-------------------");
Console.Write(string.Format(" {0,2} {1,-15}", "ID", "PlayerName"));
Console.Write("-------------------");
foreach(var c in m_Clients)
{
var client = c.Value;
Console.Write(string.Format(" {0:00} {1,-15}", client.id, client.playerSettings.playerName));
}
Console.Write("-------------------");
Console.Write(string.Format("Total: {0}/{0} players connected", m_Clients.Count, serverMaxClients.IntValue));
}
string MakeServername()
{
var f = new string[] { "Ultimate", "Furry", "Quick", "Laggy", "Hot", "Curious", "Flappy", "Sneaky", "Undercover", "Nested", "Public", "Deep", "Blue", "Hipster", "Artificial", "Bespoke" };
var l = new string[] { "Speedrun", "Fragfest", "Portal", "Framerate", "Execution", "Exception", "Pipeline", "Prefab", "Scene", "Garbage", "System", "Glacier", "Souls", "Whitespace", "Dolphin" };
var q1 = Random.Range(0, 10) == 0 ? "'" : "";
var q2 = Random.Range(0, 10) == 0 ? "'" : "";
return q1 + f[Random.Range(0, f.Length)] + q1 + " " + q2 + l[Random.Range(0, l.Length)] + q2;
}
void OnDebugDrawGameloopInfo()
{
//DebugOverlay.Write(2,2,"Server Gameloop Info:");
//var y = 3;
//DebugOverlay.Write(2, y++, " Simulation time average : {0}", m_NetworkServer.simStats.simTime);
//DebugOverlay.Write(2, y++, " Simulation time stdev : {0}", m_NetworkServer.simStats.simTimeStdDev);
//DebugOverlay.Write(2, y++, " Simulation time peek : {0}", m_NetworkServer.simStats.simTimeMax);
//y++;
//DebugOverlay.Write(2, y++, " Delta time average : {0}", m_NetworkServer.simStats.deltaTime);
//DebugOverlay.Write(2, y++, " Delta time stdev : {0}", m_NetworkServer.simStats.deltaTimeStdDev);
//DebugOverlay.Write(2, y++, " Delta time peek : {0}", m_NetworkServer.simStats.deltaTimeMax);
//y += 2;
//foreach (var clientId in m_NetworkServer.clients)
//{
// var info = m_NetworkServer.GetClientConnectionInfo(clientId);
// DebugOverlay.Write(2, y++, " addr: {0} port: {1} rtt: {2} ms", info.address, info.port, info.rtt);
//}
}
// Statemachine
enum ServerState
{
Idle,
Loading,
Active,
}
StateMachine<ServerState> m_StateMachine;
public class ClientInfo
{
public int id;
public PlayerSettings playerSettings = new PlayerSettings();
public bool isReady;
public PlayerState player;
public UserCommand latestCommand = UserCommand.defaultCommand;
}
NetworkServer m_NetworkServer;
GameWorld m_GameWorld;
NetworkStatisticsServer m_NetworkStatistics;
NetworkCompressionModel m_Model = NetworkCompressionModel.DefaultModel;
#if USE_UNET
UNETTransport m_NetworkTransport;
UNETServerBroadcast m_ServerBroadcast;
#else
SocketTransport m_NetworkTransport;
#endif
BundledResourceManager m_resourceSystem;
ChatSystemServer m_ChatSystem;
Dictionary<int, ClientInfo> m_Clients = new Dictionary<int, ClientInfo>();
ServerGameWorld m_serverGameWorld;
public double m_nextTickTime = 0;
string m_RequestedGameMode = "deathmatch";
long m_SimStartTime;
int m_SimStartTimeTick;
float m_LastSimTime;
bool m_performLateUpdate;
SQPServer m_ServerQueryProtocolServer;
[ConfigVar(Name = "show.gameloopinfo", DefaultValue = "0", Description = "Show gameloop info")]
static ConfigVar showGameLoopInfo;
[ConfigVar(Name = "server.port", DefaultValue = "7913", Description = "Port listened to by server")]
static ConfigVar serverPort;
[ConfigVar(Name = "server.quitwhenempty", DefaultValue = "0", Description = "If enabled, quit when last client disconnects.")]
static ConfigVar serverQuitWhenEmpty;
[ConfigVar(Name = "server.recycleinterval", DefaultValue = "0", Description = "Exit when N seconds old AND when 0 players. 0 means never.")]
static ConfigVar serverRecycleInterval;
[ConfigVar(Name = "server.sqp_port", DefaultValue = "7912", Description = "Port used for server query protocol")]
static ConfigVar serverSQPPort;
[ConfigVar(Name = "debug.servertickstats", DefaultValue = "0", Description = "Show stats about how many ticks we run per Unity update (headless only)")]
static ConfigVar debugServerTickStats;
[ConfigVar(Name = "server.maxclients", DefaultValue = "32", Description = "Maximum allowed clients")]
static ConfigVar serverMaxClients;
[ConfigVar(Name = "server.servername", DefaultValue = "", Description = "Servername")]
static ConfigVar serverServerName;
float m_ServerStartTime;
int m_MaxClients;
}