<commit_before>using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Mirror;

/// <summary>
/// Main API for dealing with spawning players and related things.
/// For spawning of non-player things, see Spawn.
/// </summary>
public static class PlayerSpawn
{
	/// <summary>
	/// Server-side only. For use when a player has only joined (as a JoinedViewer) and
	/// is not in control of any mobs. Spawns the joined viewer as the indicated occupation and transfers control to it.
	/// Note that this doesn't take into account game mode or antags, it just spawns whatever is requested.
	/// </summary>
	/// <param name="joinedViewer">viewer who should control the player</param>
	/// <param name="occupation">occupation to spawn as</param>
	/// <param name="characterSettings">settings to use for the character</param>
	/// <returns>the game object of the spawned player</returns>
	public static GameObject ServerSpawnPlayer(JoinedViewer joinedViewer, Occupation occupation, CharacterSettings characterSettings)
	{
		NetworkConnection conn = joinedViewer.connectionToClient;

		var newPlayer = ServerSpawnInternal(conn, occupation, characterSettings, null);
		if (newPlayer)
		{
			if (occupation.JobType != JobType.SYNDICATE &&
				occupation.JobType != JobType.AI)
			{
				SecurityRecordsManager.Instance.AddRecord(newPlayer.GetComponent<PlayerScript>(), occupation.JobType);
			}
		}

		return newPlayer;
	}


	/// <summary>
	/// Server-side only. For use when a player has only joined (as a JoinedViewer) and
	/// is not in control of any mobs. Spawns the joined viewer as the indicated occupation and transfers control to it.
	/// Note that this doesn't take into account game mode or antags, it just spawns whatever is requested.
	/// </summary>
	/// <param name="spawnRequest">details of the requested spawn</param>
	/// <returns>the game object of the spawned player</returns>
	public static GameObject ServerSpawnPlayer(PlayerSpawnRequest spawnRequest)
	{
		return ServerSpawnPlayer(spawnRequest.JoinedViewer, spawnRequest.RequestedOccupation,
			spawnRequest.CharacterSettings);
	}

	/// <summary>
	/// For use when player is connected and dead.
	/// Respawns the mind's character and transfers their control to it.
	/// </summary>
	/// <param name="forMind"></param>
	public static void ServerRespawnPlayer(Mind forMind)
	{
		if (forMind.IsSpectator)
			return;

		//get the settings from the mind
		var occupation = forMind.occupation;
		var oldBody = forMind.GetCurrentMob();
		var connection = oldBody.GetComponent<NetworkIdentity>().connectionToClient;
		var settings = oldBody.GetComponent<PlayerScript>().characterSettings;
		var oldGhost = forMind.ghost;

		ServerSpawnInternal(connection, occupation, settings, forMind, willDestroyOldBody: oldGhost != null);

		if (oldGhost)
		{
			Despawn.ServerSingle(oldGhost.gameObject);
		}

	}

	/// <summary>
	/// For use when a player is alive or dead and you want to clone their body and transfer their control to it.
	///
	/// Clones a given mind's current body to a new body and transfers control of the mind's connection to that new body.
	/// </summary>
	/// <param name="forMind"></param>
	/// <param name="worldPosition"></param>
	public static void ServerClonePlayer(Mind forMind, Vector3Int worldPosition)
	{
		//TODO: Can probably remove characterSettings from cloningrecord
		//determine previous occupation / settings
		var oldBody = forMind.GetCurrentMob();
		var occupation = forMind.occupation;
		var connection = oldBody.GetComponent<NetworkIdentity>().connectionToClient;
		var settings = oldBody.GetComponent<PlayerScript>().characterSettings;

		ServerSpawnInternal(connection, occupation, settings, forMind, worldPosition, true);
	}

	/// <summary>
	/// Spawns a new player character and transfers the connection's control into the new body.
	/// If existingMind is null, creates the new mind and assigns it to the new body.
	///
	/// Fires server and client side player spawn hooks.
	/// </summary>
	/// <param name="connection">connection to give control to the new player character</param>
	/// <param name="occupation">occupation of the new player character</param>
	/// <param name="characterSettings">settings of the new player character</param>
	/// <param name="existingMind">existing mind to transfer to the new player, if null new mind will be created
	/// and assigned to the new player character</param>
	/// <param name="spawnPos">world position to spawn at</param>
	/// <param name="naked">If spawning a player, should the player spawn without the defined initial equipment for their occupation?</param>
	/// <param name="willDestroyOldBody">if true, indicates the old body is going to be destroyed rather than pooled,
	/// thus we shouldn't send any network message which reference's the old body's ID since it won't exist.</param>
	///
	/// <returns>the spawned object</returns>
	private static GameObject ServerSpawnInternal(NetworkConnection connection, Occupation occupation, CharacterSettings characterSettings,
		Mind existingMind, Vector3Int? spawnPos = null, bool naked = false, bool willDestroyOldBody = false)
	{
		//determine where to spawn them
		if (spawnPos == null)
		{
			Transform spawnTransform = GetSpawnForJob(occupation.JobType);
			if (spawnTransform == null)
			{
				Logger.LogErrorFormat(
					"Unable to determine spawn position for connection {0} occupation {1}. Cannot spawn player.",
					Category.ItemSpawn,
					connection.address, occupation.DisplayName);
				return null;
			}

			spawnPos = spawnTransform.transform.position.CutToInt();
		}

		//create the player object
		var newPlayer = ServerCreatePlayer(spawnPos.GetValueOrDefault());
		var newPlayerScript = newPlayer.GetComponent<PlayerScript>();

		//get the old body if they have one.
		var oldBody = existingMind?.GetCurrentMob();

		//transfer control to the player object
		ServerTransferPlayer(connection, newPlayer, oldBody, EVENT.PlayerSpawned, characterSettings, willDestroyOldBody);


		if (existingMind == null)
		{
			//create the mind of the player<commit_msg>Makes player spawn in arrivals after 2 min<commit_after>using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using UnityEngine;
using Mirror;

/// <summary>
/// Main API for dealing with spawning players and related things.
/// For spawning of non-player things, see Spawn.
/// </summary>
public static class PlayerSpawn
{
	/// <summary>
	/// Server-side only. For use when a player has only joined (as a JoinedViewer) and
	/// is not in control of any mobs. Spawns the joined viewer as the indicated occupation and transfers control to it.
	/// Note that this doesn't take into account game mode or antags, it just spawns whatever is requested.
	/// </summary>
	/// <param name="joinedViewer">viewer who should control the player</param>
	/// <param name="occupation">occupation to spawn as</param>
	/// <param name="characterSettings">settings to use for the character</param>
	/// <returns>the game object of the spawned player</returns>
	public static GameObject ServerSpawnPlayer(JoinedViewer joinedViewer, Occupation occupation, CharacterSettings characterSettings)
	{
		NetworkConnection conn = joinedViewer.connectionToClient;

		var newPlayer = ServerSpawnInternal(conn, occupation, characterSettings, null);
		if (newPlayer)
		{
			if (occupation.JobType != JobType.SYNDICATE &&
				occupation.JobType != JobType.AI)
			{
				SecurityRecordsManager.Instance.AddRecord(newPlayer.GetComponent<PlayerScript>(), occupation.JobType);
			}
		}

		return newPlayer;
	}


	/// <summary>
	/// Server-side only. For use when a player has only joined (as a JoinedViewer) and
	/// is not in control of any mobs. Spawns the joined viewer as the indicated occupation and transfers control to it.
	/// Note that this doesn't take into account game mode or antags, it just spawns whatever is requested.
	/// </summary>
	/// <param name="spawnRequest">details of the requested spawn</param>
	/// <returns>the game object of the spawned player</returns>
	public static GameObject ServerSpawnPlayer(PlayerSpawnRequest spawnRequest)
	{
		return ServerSpawnPlayer(spawnRequest.JoinedViewer, spawnRequest.RequestedOccupation,
			spawnRequest.CharacterSettings);
	}

	/// <summary>
	/// For use when player is connected and dead.
	/// Respawns the mind's character and transfers their control to it.
	/// </summary>
	/// <param name="forMind"></param>
	public static void ServerRespawnPlayer(Mind forMind)
	{
		if (forMind.IsSpectator)
			return;

		//get the settings from the mind
		var occupation = forMind.occupation;
		var oldBody = forMind.GetCurrentMob();
		var connection = oldBody.GetComponent<NetworkIdentity>().connectionToClient;
		var settings = oldBody.GetComponent<PlayerScript>().characterSettings;
		var oldGhost = forMind.ghost;

		ServerSpawnInternal(connection, occupation, settings, forMind, willDestroyOldBody: oldGhost != null);

		if (oldGhost)
		{
			Despawn.ServerSingle(oldGhost.gameObject);
		}

	}

	/// <summary>
	/// For use when a player is alive or dead and you want to clone their body and transfer their control to it.
	///
	/// Clones a given mind's current body to a new body and transfers control of the mind's connection to that new body.
	/// </summary>
	/// <param name="forMind"></param>
	/// <param name="worldPosition"></param>
	public static void ServerClonePlayer(Mind forMind, Vector3Int worldPosition)
	{
		//TODO: Can probably remove characterSettings from cloningrecord
		//determine previous occupation / settings
		var oldBody = forMind.GetCurrentMob();
		var occupation = forMind.occupation;
		var connection = oldBody.GetComponent<NetworkIdentity>().connectionToClient;
		var settings = oldBody.GetComponent<PlayerScript>().characterSettings;

		ServerSpawnInternal(connection, occupation, settings, forMind, worldPosition, true);
	}

	//Jobs that should always use their own spawn points regardless of current round time
	private static readonly ReadOnlyCollection<JobType> NEVER_SPAWN_ARRIVALS_JOBS = new ReadOnlyCollection<JobType>(new List<JobType>{JobType.AI, JobType.SYNDICATE});

	/// <summary>
	/// Spawns a new player character and transfers the connection's control into the new body.
	/// If existingMind is null, creates the new mind and assigns it to the new body.
	///
	/// Fires server and client side player spawn hooks.
	/// </summary>
	/// <param name="connection">connection to give control to the new player character</param>
	/// <param name="occupation">occupation of the new player character</param>
	/// <param name="characterSettings">settings of the new player character</param>
	/// <param name="existingMind">existing mind to transfer to the new player, if null new mind will be created
	/// and assigned to the new player character</param>
	/// <param name="spawnPos">world position to spawn at</param>
	/// <param name="naked">If spawning a player, should the player spawn without the defined initial equipment for their occupation?</param>
	/// <param name="willDestroyOldBody">if true, indicates the old body is going to be destroyed rather than pooled,
	/// thus we shouldn't send any network message which reference's the old body's ID since it won't exist.</param>
	///
	/// <returns>the spawned object</returns>
	private static GameObject ServerSpawnInternal(NetworkConnection connection, Occupation occupation, CharacterSettings characterSettings,
		Mind existingMind, Vector3Int? spawnPos = null, bool naked = false, bool willDestroyOldBody = false)
	{
		//determine where to spawn them
		if (spawnPos == null)
		{
			Transform spawnTransform;
			//Spawn normal location for special jobs or if 2 minutes havn't passed
			if (System.DateTime.Today.AddHours(12).AddMinutes(2) > GameManager.Instance.stationTime || NEVER_SPAWN_ARRIVALS_JOBS.Contains(occupation.JobType))
			{
				 spawnTransform = GetSpawnForJob(occupation.JobType);
			}
			else
			{
				spawnTransform = GetSpawnForJob(JobType.ASSISTANT);
			}
			
			if (spawnTransform == null)
			{
				Logger.LogErrorFormat(
					"Unable to determine spawn position for connection {0} occupation {1}. Cannot spawn player.",
					Category.ItemSpawn,
					connection.address, occupation.DisplayName);
				return null;
			}

			spawnPos = spawnTransform.transform.position.CutToInt();
		}

		//create the player object
		var newPlayer = ServerCreatePlayer(spawnPos.GetValueOrDefault());
		var newPlayerScript = newPlayer.GetComponent<PlayerScript>();

		//get the old body if they have one.
		var oldBody = existingMind?.GetCurrentMob();

		//transfer control to the player object
		ServerTransferPlayer(connection, newPlayer, oldBody, EVENT.PlayerSpawned, characterSettings, willDestroyOldBody);


		if (existingMind == null)
		{
			//create the mind of the player
{'fraction_non_alphanumeric': 0.1005464480874317, 'fraction_numerical': 0.001014832162373146, 'mean_word_length': 4.201380430369468, 'pattern_counts': {'":': 0, '<': 110, '<?xml version=': 0, '>': 111, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3828670', 'n_tokens_mistral': 3800, 'n_tokens_neox': 3503, 'n_words': 1255}