Decompiled source of ServerCharacters v1.4.14

ServerCharacters.dll

Decompiled 7 months ago
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Sockets;
using System.Numerics;
using System.Numerics.Hashing;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using FxResources.System.Buffers;
using FxResources.System.Collections.Immutable;
using FxResources.System.Memory;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ProtoBuf;
using ProtoBuf.Compiler;
using ProtoBuf.Extensions;
using ProtoBuf.Internal;
using ProtoBuf.Internal.Serializers;
using ProtoBuf.Meta;
using ProtoBuf.Serializers;
using ProtoBuf.WellKnownTypes;
using ServerSync;
using Steamworks;
using TMPro;
using UnityEngine;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ServerCharacters")]
[assembly: AssemblyDescription("https://valheim.thunderstore.io/package/Smoothbrain/ServerCharacters")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("ServerCharacters")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("B6DC411F-BB68-4745-BA9E-ADFEAF8C7F0E")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyCompany("")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[<1d3fe844-d473-4f59-814d-29cc5eee076d>Embedded]
	internal sealed class <1d3fe844-d473-4f59-814d-29cc5eee076d>EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[<1d3fe844-d473-4f59-814d-29cc5eee076d>Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class <b871943b-bae9-475c-9342-11e13c80a649>NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public <b871943b-bae9-475c-9342-11e13c80a649>NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public <b871943b-bae9-475c-9342-11e13c80a649>NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[<1d3fe844-d473-4f59-814d-29cc5eee076d>Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class <b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public <b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace ServerCharacters
{
	public static class ClientSide
	{
		[HarmonyPatch(typeof(Skills), "ResetSkill")]
		public class SaveLastSkillReset
		{
			public static SkillType last = (SkillType)999;

			[UsedImplicitly]
			public static void Finalizer(SkillType skillType)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				last = skillType;
			}
		}

		[HarmonyPatch(typeof(Console), "Print")]
		public class SilenceConsole
		{
			public static bool silence;

			[UsedImplicitly]
			public static bool Prefix()
			{
				return !silence;
			}
		}

		[HarmonyPatch(typeof(Terminal), "InitTerminal")]
		public class AddChatCommands
		{
			[Serializable]
			[CompilerGenerated]
			private sealed class <>c
			{
				public static readonly <>c <>9 = new <>c();

				public static Func<KeyValuePair<SkillType, Skill>, SkillType> <>9__0_3;

				public static Func<KeyValuePair<SkillType, Skill>, Skill> <>9__0_4;

				public static ConsoleEvent <>9__0_0;

				public static ConsoleOptionsFetcher <>9__0_1;

				internal void <Postfix>b__0_0(ConsoleEventArgs args)
				{
					//IL_0067: Unknown result type (might be due to invalid IL or missing references)
					//IL_0071: Invalid comparison between Unknown and I4
					//IL_023a: Unknown result type (might be due to invalid IL or missing references)
					//IL_0244: Invalid comparison between Unknown and I4
					//IL_081e: Unknown result type (might be due to invalid IL or missing references)
					<>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0
					{
						args = args
					};
					if (!ServerCharacters.configSync.IsAdmin)
					{
						<>c__DisplayClass0_.args.Context.AddString("You are not an admin on this server.");
					}
					else if (<>c__DisplayClass0_.args.Length >= 3 && <>c__DisplayClass0_.args[1] == "resetskill")
					{
						if ((int)<Postfix>g__GetSkillType|0_2(<>c__DisplayClass0_.args[2]) == 999)
						{
							<>c__DisplayClass0_.args.Context.AddString(<>c__DisplayClass0_.args[2] + " is not a valid skill.");
						}
						else if (<>c__DisplayClass0_.args.Length > 3)
						{
							string text = <>c__DisplayClass0_.args[3];
							int num = 4;
							if (text.StartsWith("\"", StringComparison.Ordinal))
							{
								text = text.Substring(1);
								while (num < <>c__DisplayClass0_.args.Length && !text.EndsWith("\"", StringComparison.Ordinal))
								{
									text = text + " " + <>c__DisplayClass0_.args[num++];
								}
								text = text.Substring(0, text.Length - 1);
							}
							ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters ResetSkill", new object[3]
							{
								<>c__DisplayClass0_.args[2],
								text,
								(<>c__DisplayClass0_.args.Length > num) ? <>c__DisplayClass0_.args[num] : "0"
							});
							<>c__DisplayClass0_.args.Context.AddString(<>c__DisplayClass0_.args[2] + " has been reset for " + text + ".");
						}
						else
						{
							ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters ResetSkill", new object[3]
							{
								<>c__DisplayClass0_.args[2],
								"",
								"0"
							});
							<>c__DisplayClass0_.args.Context.AddString(<>c__DisplayClass0_.args[2] + " has been reset for everyone.");
						}
					}
					else if (<>c__DisplayClass0_.args.Length >= 3 && <>c__DisplayClass0_.args[1] == "raiseskill")
					{
						if ((int)<Postfix>g__GetSkillType|0_2(<>c__DisplayClass0_.args[2]) == 999)
						{
							<>c__DisplayClass0_.args.Context.AddString(<>c__DisplayClass0_.args[2] + " is not a valid skill.");
						}
						else if (<>c__DisplayClass0_.args.Length > 4)
						{
							string text2 = <>c__DisplayClass0_.args[4];
							int num2 = 5;
							if (text2.StartsWith("\"", StringComparison.Ordinal))
							{
								text2 = text2.Substring(1);
								while (num2 < <>c__DisplayClass0_.args.Length && !text2.EndsWith("\"", StringComparison.Ordinal))
								{
									text2 = text2 + " " + <>c__DisplayClass0_.args[num2++];
								}
								text2 = text2.Substring(0, text2.Length - 1);
							}
							ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters RaiseSkill", new object[4]
							{
								<>c__DisplayClass0_.args[2],
								int.Parse(<>c__DisplayClass0_.args[3]),
								text2,
								(<>c__DisplayClass0_.args.Length > num2) ? <>c__DisplayClass0_.args[num2] : "0"
							});
							<>c__DisplayClass0_.args.Context.AddString(<>c__DisplayClass0_.args[2] + " has been raised by " + <>c__DisplayClass0_.args[3] + " for " + text2 + ".");
						}
						else
						{
							ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters RaiseSkill", new object[4]
							{
								<>c__DisplayClass0_.args[2],
								int.Parse(<>c__DisplayClass0_.args[3]),
								"",
								"0"
							});
							<>c__DisplayClass0_.args.Context.AddString(<>c__DisplayClass0_.args[2] + " has been raised by " + <>c__DisplayClass0_.args[3] + " for everyone.");
						}
					}
					else if (<>c__DisplayClass0_.args.Length >= 3 && <>c__DisplayClass0_.args[1] == "giveitem")
					{
						if (ObjectDB.instance.GetItemPrefab(<>c__DisplayClass0_.args[2]) == null)
						{
							<>c__DisplayClass0_.args.Context.AddString(<>c__DisplayClass0_.args[2] + " is not a valid item.");
						}
						else if (<>c__DisplayClass0_.args.Length > 4)
						{
							string text3 = <>c__DisplayClass0_.args[4];
							int num3 = 5;
							if (text3.StartsWith("\"", StringComparison.Ordinal))
							{
								text3 = text3.Substring(1);
								while (num3 < <>c__DisplayClass0_.args.Length && !text3.EndsWith("\"", StringComparison.Ordinal))
								{
									text3 = text3 + " " + <>c__DisplayClass0_.args[num3++];
								}
								text3 = text3.Substring(0, text3.Length - 1);
							}
							ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters GiveItem", new object[4]
							{
								<>c__DisplayClass0_.args[2],
								int.Parse(<>c__DisplayClass0_.args[3]),
								text3,
								(<>c__DisplayClass0_.args.Length > num3) ? <>c__DisplayClass0_.args[num3] : "0"
							});
							<>c__DisplayClass0_.args.Context.AddString(<>c__DisplayClass0_.args[3] + "x " + <>c__DisplayClass0_.args[2] + " has been given to " + text3 + ", if their inventory isn't full.");
						}
						else
						{
							<>c__DisplayClass0_.args.Context.AddString("Please specify a target player.");
						}
					}
					else if (<>c__DisplayClass0_.args.Length >= 3 && <>c__DisplayClass0_.args[1] == "teleport")
					{
						string text4 = <>c__DisplayClass0_.args[2];
						int num4 = 3;
						if (text4.StartsWith("\"", StringComparison.Ordinal))
						{
							text4 = text4.Substring(1);
							while (num4 < <>c__DisplayClass0_.args.Length && !text4.EndsWith("\"", StringComparison.Ordinal))
							{
								text4 = text4 + " " + <>c__DisplayClass0_.args[num4++];
							}
							text4 = text4.Substring(0, text4.Length - 1);
						}
						ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters GetPlayerPos", new object[2]
						{
							text4,
							(<>c__DisplayClass0_.args.Length > num4) ? <>c__DisplayClass0_.args[num4] : "0"
						});
						((MonoBehaviour)ServerCharacters.selfReference).StartCoroutine(<>c__DisplayClass0_.<Postfix>g__AwaitResponse|5());
					}
					else if (<>c__DisplayClass0_.args.Length >= 3 && <>c__DisplayClass0_.args[1] == "summon")
					{
						string text5 = <>c__DisplayClass0_.args[2];
						int num5 = 3;
						if (text5.StartsWith("\"", StringComparison.Ordinal))
						{
							text5 = text5.Substring(1);
							while (num5 < <>c__DisplayClass0_.args.Length && !text5.EndsWith("\"", StringComparison.Ordinal))
							{
								text5 = text5 + " " + <>c__DisplayClass0_.args[num5++];
							}
							text5 = text5.Substring(0, text5.Length - 1);
						}
						ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters SendOwnPos", new object[3]
						{
							text5,
							(<>c__DisplayClass0_.args.Length > num5) ? <>c__DisplayClass0_.args[num5] : "0",
							((Component)Player.m_localPlayer).transform.position
						});
						((MonoBehaviour)ServerCharacters.selfReference).StartCoroutine(<>c__DisplayClass0_.<Postfix>g__AwaitResponse|7());
					}
					else
					{
						<>c__DisplayClass0_.args.Context.AddString("ServerCharacters console commands - use 'ServerCharacters' followed by one of the following options.");
						<>c__DisplayClass0_.args.Context.AddString("resetskill [skillname] [playername] [id] - resets the skill for the specified player. Steam / Xbox ID is optional and only required, if multiple players have the same name. If no name is provided, the skill is reset for every character on the server, online and offline.");
						<>c__DisplayClass0_.args.Context.AddString("raiseskill [skillname] [level] [playername] [id] - raises the skill for the specified player by the specified level. Steam / Xbox ID is optional and only required, if multiple players have the same name. If no name is provided, the skill is raised for every character on the server, online and offline.");
						<>c__DisplayClass0_.args.Context.AddString("teleport [playername] [steamid] - teleports you to the specified player. Quote names with a space. Steam / Xbox ID is optional and only required, if multiple players have the same name.");
						<>c__DisplayClass0_.args.Context.AddString("summon [playername] [steamid] - teleports the specified player to you. Quote names with a space. Steam / Xbox ID is optional and only required, if multiple players have the same name.");
						<>c__DisplayClass0_.args.Context.AddString("giveitem [itemname] [quantity] [playername] [id] - adds the specified item to the specified players inventory in the specified quantity. Quote names with a space. Steam / Xbox ID is optional and only required, if multiple players have the same name. Will fail, if their inventory is full.");
					}
				}

				internal SkillType <Postfix>b__0_3(KeyValuePair<SkillType, Skill> kv)
				{
					//IL_0002: Unknown result type (might be due to invalid IL or missing references)
					return kv.Key;
				}

				internal Skill <Postfix>b__0_4(KeyValuePair<SkillType, Skill> kv)
				{
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0011: Unknown result type (might be due to invalid IL or missing references)
					//IL_0023: Unknown result type (might be due to invalid IL or missing references)
					//IL_0036: Expected O, but got Unknown
					return new Skill(kv.Value.m_info)
					{
						m_accumulator = kv.Value.m_accumulator,
						m_level = kv.Value.m_level
					};
				}

				internal List<string> <Postfix>b__0_1()
				{
					return new List<string> { "resetskill", "teleport", "summon", "raiseskill", "giveitem" };
				}
			}

			[CompilerGenerated]
			private sealed class <>c__DisplayClass0_0
			{
				public ConsoleEventArgs args;
			}

			private static void Postfix()
			{
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Expected O, but got Unknown
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: Expected O, but got Unknown
				object obj = <>c.<>9__0_0;
				if (obj == null)
				{
					ConsoleEvent val = delegate(ConsoleEventArgs args)
					{
						//IL_0067: Unknown result type (might be due to invalid IL or missing references)
						//IL_0071: Invalid comparison between Unknown and I4
						//IL_023a: Unknown result type (might be due to invalid IL or missing references)
						//IL_0244: Invalid comparison between Unknown and I4
						//IL_081e: Unknown result type (might be due to invalid IL or missing references)
						if (!ServerCharacters.configSync.IsAdmin)
						{
							args.Context.AddString("You are not an admin on this server.");
						}
						else if (args.Length >= 3 && args[1] == "resetskill")
						{
							if ((int)GetSkillType(args[2]) == 999)
							{
								args.Context.AddString(args[2] + " is not a valid skill.");
							}
							else if (args.Length > 3)
							{
								string text = args[3];
								int num = 4;
								if (text.StartsWith("\"", StringComparison.Ordinal))
								{
									text = text.Substring(1);
									while (num < args.Length && !text.EndsWith("\"", StringComparison.Ordinal))
									{
										text = text + " " + args[num++];
									}
									text = text.Substring(0, text.Length - 1);
								}
								ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters ResetSkill", new object[3]
								{
									args[2],
									text,
									(args.Length > num) ? args[num] : "0"
								});
								args.Context.AddString(args[2] + " has been reset for " + text + ".");
							}
							else
							{
								ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters ResetSkill", new object[3]
								{
									args[2],
									"",
									"0"
								});
								args.Context.AddString(args[2] + " has been reset for everyone.");
							}
						}
						else if (args.Length >= 3 && args[1] == "raiseskill")
						{
							if ((int)GetSkillType(args[2]) == 999)
							{
								args.Context.AddString(args[2] + " is not a valid skill.");
							}
							else if (args.Length > 4)
							{
								string text2 = args[4];
								int num2 = 5;
								if (text2.StartsWith("\"", StringComparison.Ordinal))
								{
									text2 = text2.Substring(1);
									while (num2 < args.Length && !text2.EndsWith("\"", StringComparison.Ordinal))
									{
										text2 = text2 + " " + args[num2++];
									}
									text2 = text2.Substring(0, text2.Length - 1);
								}
								ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters RaiseSkill", new object[4]
								{
									args[2],
									int.Parse(args[3]),
									text2,
									(args.Length > num2) ? args[num2] : "0"
								});
								args.Context.AddString(args[2] + " has been raised by " + args[3] + " for " + text2 + ".");
							}
							else
							{
								ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters RaiseSkill", new object[4]
								{
									args[2],
									int.Parse(args[3]),
									"",
									"0"
								});
								args.Context.AddString(args[2] + " has been raised by " + args[3] + " for everyone.");
							}
						}
						else if (args.Length >= 3 && args[1] == "giveitem")
						{
							if (ObjectDB.instance.GetItemPrefab(args[2]) == null)
							{
								args.Context.AddString(args[2] + " is not a valid item.");
							}
							else if (args.Length > 4)
							{
								string text3 = args[4];
								int num3 = 5;
								if (text3.StartsWith("\"", StringComparison.Ordinal))
								{
									text3 = text3.Substring(1);
									while (num3 < args.Length && !text3.EndsWith("\"", StringComparison.Ordinal))
									{
										text3 = text3 + " " + args[num3++];
									}
									text3 = text3.Substring(0, text3.Length - 1);
								}
								ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters GiveItem", new object[4]
								{
									args[2],
									int.Parse(args[3]),
									text3,
									(args.Length > num3) ? args[num3] : "0"
								});
								args.Context.AddString(args[3] + "x " + args[2] + " has been given to " + text3 + ", if their inventory isn't full.");
							}
							else
							{
								args.Context.AddString("Please specify a target player.");
							}
						}
						else if (args.Length >= 3 && args[1] == "teleport")
						{
							string text4 = args[2];
							int num4 = 3;
							if (text4.StartsWith("\"", StringComparison.Ordinal))
							{
								text4 = text4.Substring(1);
								while (num4 < args.Length && !text4.EndsWith("\"", StringComparison.Ordinal))
								{
									text4 = text4 + " " + args[num4++];
								}
								text4 = text4.Substring(0, text4.Length - 1);
							}
							ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters GetPlayerPos", new object[2]
							{
								text4,
								(args.Length > num4) ? args[num4] : "0"
							});
							((MonoBehaviour)ServerCharacters.selfReference).StartCoroutine(AwaitResponse());
						}
						else if (args.Length >= 3 && args[1] == "summon")
						{
							string text5 = args[2];
							int num5 = 3;
							if (text5.StartsWith("\"", StringComparison.Ordinal))
							{
								text5 = text5.Substring(1);
								while (num5 < args.Length && !text5.EndsWith("\"", StringComparison.Ordinal))
								{
									text5 = text5 + " " + args[num5++];
								}
								text5 = text5.Substring(0, text5.Length - 1);
							}
							ZNet.instance.GetServerPeer().m_rpc.Invoke("ServerCharacters SendOwnPos", new object[3]
							{
								text5,
								(args.Length > num5) ? args[num5] : "0",
								((Component)Player.m_localPlayer).transform.position
							});
							((MonoBehaviour)ServerCharacters.selfReference).StartCoroutine(AwaitResponse());
						}
						else
						{
							args.Context.AddString("ServerCharacters console commands - use 'ServerCharacters' followed by one of the following options.");
							args.Context.AddString("resetskill [skillname] [playername] [id] - resets the skill for the specified player. Steam / Xbox ID is optional and only required, if multiple players have the same name. If no name is provided, the skill is reset for every character on the server, online and offline.");
							args.Context.AddString("raiseskill [skillname] [level] [playername] [id] - raises the skill for the specified player by the specified level. Steam / Xbox ID is optional and only required, if multiple players have the same name. If no name is provided, the skill is raised for every character on the server, online and offline.");
							args.Context.AddString("teleport [playername] [steamid] - teleports you to the specified player. Quote names with a space. Steam / Xbox ID is optional and only required, if multiple players have the same name.");
							args.Context.AddString("summon [playername] [steamid] - teleports the specified player to you. Quote names with a space. Steam / Xbox ID is optional and only required, if multiple players have the same name.");
							args.Context.AddString("giveitem [itemname] [quantity] [playername] [id] - adds the specified item to the specified players inventory in the specified quantity. Quote names with a space. Steam / Xbox ID is optional and only required, if multiple players have the same name. Will fail, if their inventory is full.");
						}
						[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
						IEnumerator AwaitResponse()
						{
							awaitingPos = new TaskCompletionSource<Vector3>();
							Task<Vector3> task2 = awaitingPos.Task;
							yield return (object)new WaitUntil((Func<bool>)(() => task2.IsCompleted));
							Vector3 result2 = task2.Result;
							if (result2 == Vector3.zero)
							{
								args.Context.AddString("A player with this name is not online.");
							}
							else
							{
								((Character)Player.m_localPlayer).TeleportTo(result2, Quaternion.identity, true);
							}
						}
						[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
						IEnumerator AwaitResponse()
						{
							awaitingPos = new TaskCompletionSource<Vector3>();
							Task<Vector3> task = awaitingPos.Task;
							yield return (object)new WaitUntil((Func<bool>)(() => task.IsCompleted));
							Vector3 result = task.Result;
							args.Context.AddString((result == Vector3.zero) ? "A player with this name is not online." : "The player is being summoned, please wait a second.");
						}
					};
					<>c.<>9__0_0 = val;
					obj = (object)val;
				}
				object obj2 = <>c.<>9__0_1;
				if (obj2 == null)
				{
					ConsoleOptionsFetcher val2 = () => new List<string> { "resetskill", "teleport", "summon", "raiseskill", "giveitem" };
					<>c.<>9__0_1 = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("ServerCharacters", "Manages the ServerCharacters commands.", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false);
				[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
				static SkillType GetSkillType(string name)
				{
					//IL_005a: Unknown result type (might be due to invalid IL or missing references)
					//IL_0085: Unknown result type (might be due to invalid IL or missing references)
					Dictionary<SkillType, Skill> skillData = ((Character)Player.m_localPlayer).GetSkills().m_skillData;
					Dictionary<SkillType, Skill> src = ((IEnumerable<KeyValuePair<SkillType, Skill>>)skillData).ToDictionary((Func<KeyValuePair<SkillType, Skill>, SkillType>)((KeyValuePair<SkillType, Skill> kv) => kv.Key), (Func<KeyValuePair<SkillType, Skill>, Skill>)((KeyValuePair<SkillType, Skill> kv) => new Skill(kv.Value.m_info)
					{
						m_accumulator = kv.Value.m_accumulator,
						m_level = kv.Value.m_level
					}));
					SaveLastSkillReset.last = (SkillType)999;
					try
					{
						SilenceConsole.silence = true;
						((Character)Player.m_localPlayer).GetSkills().CheatResetSkill(name);
					}
					finally
					{
						SilenceConsole.silence = false;
					}
					Utils.OverwriteDict(src, skillData);
					return SaveLastSkillReset.last;
				}
			}

			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			[CompilerGenerated]
			internal static SkillType <Postfix>g__GetSkillType|0_2(string name)
			{
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0085: Unknown result type (might be due to invalid IL or missing references)
				Dictionary<SkillType, Skill> skillData = ((Character)Player.m_localPlayer).GetSkills().m_skillData;
				Dictionary<SkillType, Skill> src = ((IEnumerable<KeyValuePair<SkillType, Skill>>)skillData).ToDictionary((Func<KeyValuePair<SkillType, Skill>, SkillType>)((KeyValuePair<SkillType, Skill> kv) => kv.Key), (Func<KeyValuePair<SkillType, Skill>, Skill>)((KeyValuePair<SkillType, Skill> kv) => new Skill(kv.Value.m_info)
				{
					m_accumulator = kv.Value.m_accumulator,
					m_level = kv.Value.m_level
				}));
				SaveLastSkillReset.last = (SkillType)999;
				try
				{
					SilenceConsole.silence = true;
					((Character)Player.m_localPlayer).GetSkills().CheatResetSkill(name);
				}
				finally
				{
					SilenceConsole.silence = false;
				}
				Utils.OverwriteDict(src, skillData);
				return SaveLastSkillReset.last;
			}
		}

		[HarmonyPatch(typeof(PlayerProfile), "SavePlayerData")]
		private static class PatchPlayerProfilePlayerSave
		{
			[UsedImplicitly]
			private static void Prefix()
			{
				if (serverCharacter && doEmergencyBackup && playerSnapShotLast != null)
				{
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Humanoid)localPlayer).m_inventory.m_inventory = playerSnapShotLast.inventory;
						Utils.OverwriteDict(playerSnapShotLast.knownStations, localPlayer.m_knownStations);
						Utils.OverwriteDict(playerSnapShotLast.knownTexts, localPlayer.m_knownTexts);
					}
				}
			}
		}

		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
		[HarmonyPatch(typeof(PlayerProfile), "SavePlayerToDisk")]
		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
		private static class PatchPlayerProfileSave_Client
		{
			private static readonly MethodInfo ArrayWriter = AccessTools.DeclaredMethod(typeof(BinaryWriter), "Write", new Type[1] { typeof(byte[]) }, (Type[])null);

			private static readonly MethodInfo ServerCharacterSaver = AccessTools.DeclaredMethod(typeof(PatchPlayerProfileSave_Client), "SaveCharacterToServer", (Type[])null, (Type[])null);

			private static byte[] SaveCharacterToServer(byte[] packageArray, PlayerProfile profile)
			{
				if (serverCharacter && doEmergencyBackup && serverEncryptionKey != null)
				{
					string characterSavePath = Utils.CharacterSavePath;
					char directorySeparatorChar = Path.DirectorySeparatorChar;
					File.WriteAllBytes(characterSavePath + directorySeparatorChar + profile.m_filename + ".fch.signature", generateProfileSignature(packageArray, serverEncryptionKey));
					string characterSavePath2 = Utils.CharacterSavePath;
					directorySeparatorChar = Path.DirectorySeparatorChar;
					File.WriteAllBytes(characterSavePath2 + directorySeparatorChar + profile.m_filename + ".fch.serverbackup", packageArray);
					doEmergencyBackup = false;
				}
				if (serverCharacter && !currentlySaving)
				{
					ZNet instance = ZNet.instance;
					if (instance != null)
					{
						ZNetPeer serverPeer = instance.GetServerPeer();
						if (((serverPeer != null) ? new bool?(serverPeer.IsReady()) : null).GetValueOrDefault())
						{
							if (forceSynchronousSaving)
							{
								foreach (bool item in Shared.sendCompressedDataToPeer(ZNet.instance.GetServerPeer(), (iDied && ServerCharacters.hardcoreMode.GetToggle()) ? "ServerCharacters PlayerDied" : "ServerCharacters PlayerProfile", packageArray))
								{
									if (!item)
									{
										Thread.Sleep(10);
									}
								}
							}
							else
							{
								currentlySaving = true;
								((MonoBehaviour)ZNet.instance).StartCoroutine(saveAsync());
							}
							return packageArray;
						}
					}
				}
				return packageArray;
				IEnumerator saveAsync()
				{
					foreach (bool item2 in Shared.sendCompressedDataToPeer(ZNet.instance.GetServerPeer(), (iDied && ServerCharacters.hardcoreMode.GetToggle()) ? "ServerCharacters PlayerDied" : "ServerCharacters PlayerProfile", packageArray))
					{
						if (!item2)
						{
							yield return null;
						}
					}
					currentlySaving = false;
				}
			}

			[UsedImplicitly]
			private static IEnumerable<CodeInstruction> Transpiler(MethodBase method, IEnumerable<CodeInstruction> instructions)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Expected O, but got Unknown
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Expected O, but got Unknown
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: Expected O, but got Unknown
				//IL_004b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0051: Expected O, but got Unknown
				return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
				{
					new CodeMatch(new CodeInstruction(OpCodes.Callvirt, (object)ArrayWriter), (string)null)
				}).Insert((CodeInstruction[])(object)new CodeInstruction[2]
				{
					new CodeInstruction(OpCodes.Ldarg_0, (object)null),
					new CodeInstruction(OpCodes.Call, (object)ServerCharacterSaver)
				}).Instructions();
			}
		}

		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
		[HarmonyPatch]
		private class EnableSocketLinger
		{
			private static MethodInfo socketClose => AccessTools.Method(typeof(SteamNetworkingSockets), "CloseConnection", (Type[])null, (Type[])null);

			private static void dummy()
			{
			}

			private static MethodInfo TargetMethod()
			{
				Type type = Type.GetType("ZSteamSocket, assembly_valheim");
				if ((object)type == null)
				{
					return AccessTools.DeclaredMethod(typeof(EnableSocketLinger), "dummy", (Type[])null, (Type[])null);
				}
				return AccessTools.DeclaredMethod(type, "Close", (Type[])null, (Type[])null);
			}

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> codeInstructions)
			{
				foreach (CodeInstruction instruction in codeInstructions)
				{
					if (instruction.opcode == OpCodes.Call && CodeInstructionExtensions.OperandIs(instruction, (MemberInfo)socketClose))
					{
						yield return new CodeInstruction(OpCodes.Pop, (object)null);
						yield return new CodeInstruction(OpCodes.Ldc_I4_1, (object)null);
					}
					yield return instruction;
				}
			}
		}

		[HarmonyPatch(typeof(Game), "Shutdown")]
		private static class PatchGameShutdown
		{
			[UsedImplicitly]
			private static void Prefix()
			{
				forceSynchronousSaving = true;
			}

			[UsedImplicitly]
			private static void Finalizer()
			{
				serverCharacter = false;
				forceSynchronousSaving = false;
			}
		}

		[HarmonyPatch(typeof(Player), "Save")]
		private static class StorePoisonDebuff
		{
			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			private static void Prefix(Player __instance)
			{
				if (ServerCharacters.storePoison.GetToggle())
				{
					StatusEffect statusEffect = ((Character)__instance).m_seman.GetStatusEffect(StringExtensionMethods.GetStableHashCode("Poison"));
					SE_Poison val = (SE_Poison)(object)((statusEffect is SE_Poison) ? statusEffect : null);
					if (val != null && !((Character)__instance).IsDead())
					{
						__instance.m_customData["ServerCharacters PoisonDamage"] = val.m_damageLeft.ToString(CultureInfo.InvariantCulture);
						__instance.m_customData["ServerCharacters PoisonDamageHit"] = val.m_damagePerHit.ToString(CultureInfo.InvariantCulture);
						__instance.m_customData["ServerCharacters PoisonTTL"] = ((StatusEffect)val).m_ttl.ToString(CultureInfo.InvariantCulture);
					}
					else
					{
						__instance.m_customData.Remove("ServerCharacters PoisonDamage");
						__instance.m_customData.Remove("ServerCharacters PoisonDamageHit");
						__instance.m_customData.Remove("ServerCharacters PoisonTTL");
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "Load")]
		private static class LoadPoisonDebuff
		{
			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			private static void Postfix(Player __instance)
			{
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				if (((Character)__instance).m_nview.m_zdo != null && ServerCharacters.storePoison.GetToggle() && __instance.m_customData.TryGetValue("ServerCharacters PoisonDamage", out var value) && value != "")
				{
					SE_Poison val = (SE_Poison)((Character)__instance).m_seman.AddStatusEffect(StringExtensionMethods.GetStableHashCode("Poison"), false, 0, 0f);
					val.m_damageLeft = float.Parse(__instance.m_customData["ServerCharacters PoisonDamage"], CultureInfo.InvariantCulture);
					val.m_damagePerHit = float.Parse(__instance.m_customData["ServerCharacters PoisonDamageHit"], CultureInfo.InvariantCulture);
					((StatusEffect)val).m_ttl = float.Parse(__instance.m_customData["ServerCharacters PoisonTTL"], CultureInfo.InvariantCulture);
				}
			}
		}

		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
		private static class PatchZNetOnNewConnection
		{
			[UsedImplicitly]
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
				//IL_01c3: Expected O, but got Unknown
				if (ZNet.instance.IsServer())
				{
					return;
				}
				peer.m_rpc.Register<ZPackage>("ServerCharacters PlayerProfile", Shared.receiveCompressedFromPeer(onReceivedProfile));
				peer.m_rpc.Register<ZPackage>("ServerCharacters KeyExchange", (Action<ZRpc, ZPackage>)receiveEncryptionKeyFromServer);
				peer.m_rpc.Register<string>("ServerCharacters IngameMessage", (Action<ZRpc, string>)onReceivedIngameMessage);
				peer.m_rpc.Register<string>("ServerCharacters KickMessage", (Action<ZRpc, string>)onReceivedKickMessage);
				peer.m_rpc.Register<string>("ServerCharacters ResetSkill", (Action<ZRpc, string>)onReceivedResetSkill);
				peer.m_rpc.Register<string, int>("ServerCharacters RaiseSkill", (Action<ZRpc, string, int>)onReceivedRaiseSkill);
				peer.m_rpc.Register<Vector3>("ServerCharacters GetPlayerPos", (Action<ZRpc, Vector3>)onReceivedPlayerPos);
				peer.m_rpc.Register<string, int>("ServerCharacters GiveItem", (Action<ZRpc, string, int>)onReceivedGiveItem);
				peer.m_rpc.Register<Vector3>("ServerCharacters SendOwnPos", (Action<ZRpc, Vector3>)onReceivedOwnPos);
				peer.m_rpc.Register<Vector3>("ServerCharacters TeleportTo", (Action<ZRpc, Vector3>)onReceivedTeleportTo);
				string characterSavePath = Utils.CharacterSavePath;
				char directorySeparatorChar = Path.DirectorySeparatorChar;
				string path = characterSavePath + directorySeparatorChar + Game.instance.m_playerProfile.m_filename + ".fch.signature";
				string characterSavePath2 = Utils.CharacterSavePath;
				directorySeparatorChar = Path.DirectorySeparatorChar;
				string path2 = characterSavePath2 + directorySeparatorChar + Game.instance.m_playerProfile.m_filename + ".fch.serverbackup";
				if (!File.Exists(path) || !File.Exists(path2))
				{
					return;
				}
				Utils.Log("Found emergency backup and signature for character '" + Game.instance.m_playerProfile.m_filename + "'. Trying to restore the backup.");
				ZPackage val = new ZPackage();
				val.Write(File.ReadAllBytes(path2));
				val.Write(File.ReadAllBytes(path));
				foreach (bool item in Shared.sendCompressedDataToPeer(peer, "ServerCharacters CheckSignature", val.GetArray()))
				{
					if (!item)
					{
						Thread.Sleep(10);
					}
				}
			}

			private static void onReceivedOwnPos(ZRpc peerRpc, Vector3 pos)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				if (awaitingPos != null)
				{
					awaitingPos.SetResult(pos);
					awaitingPos = null;
				}
			}

			private static void onReceivedTeleportTo(ZRpc peerRpc, Vector3 pos)
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				((Character)Player.m_localPlayer).TeleportTo(pos, Quaternion.identity, true);
			}

			private static void onReceivedPlayerPos(ZRpc peerRpc, Vector3 pos)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				if (awaitingPos != null)
				{
					awaitingPos.SetResult(pos);
					awaitingPos = null;
				}
			}

			private static void onReceivedGiveItem(ZRpc peerRpc, string itemName, int itemQuantity)
			{
				string text = (((Humanoid)Player.m_localPlayer).m_inventory.AddItem(ObjectDB.instance.GetItemPrefab(itemName), itemQuantity) ? $"An admin added {itemQuantity}x {itemName} to your inventory." : $"An admin tried to add {itemQuantity}x {itemName} to your inventory, but it is full.");
				((Terminal)Chat.instance).AddString(text);
				Chat.instance.m_hideTimer = 0f;
				MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null);
			}

			private static void onReceivedResetSkill(ZRpc peerRpc, string skill)
			{
				Player.m_localPlayer.m_skills.CheatResetSkill(skill);
				string text = "An admin reset your skill " + skill;
				((Terminal)Chat.instance).AddString(text);
				Chat.instance.m_hideTimer = 0f;
				MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null);
			}

			private static void onReceivedRaiseSkill(ZRpc peerRpc, string skill, int level)
			{
				Player.m_localPlayer.m_skills.CheatRaiseSkill(skill, (float)level, true);
				string text = $"An admin raised your skill {skill} by {level}";
				((Terminal)Chat.instance).AddString(text);
				Chat.instance.m_hideTimer = 0f;
				MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null);
			}

			private static void onReceivedIngameMessage(ZRpc peerRpc, string message)
			{
				((Terminal)Chat.instance).AddString(message);
				Chat.instance.m_hideTimer = 0f;
				MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null);
			}

			private static void onReceivedKickMessage(ZRpc peerRpc, string message)
			{
				connectionError = message;
			}

			private static void onReceivedProfile(ZRpc peerRpc, byte[] profileData)
			{
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_005a: Expected O, but got Unknown
				//IL_007d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				if (profileData.Length == 0)
				{
					if (Game.instance.m_playerProfile.m_worldData.Count != 0)
					{
						Game.instance.Logout(true, true);
						ZNet.m_connectionStatus = (ConnectionStatus)5;
						connectionError = "Please create a new character, before connecting to this server, to avoid loss of data.";
					}
					else
					{
						serverCharacter = true;
						acquireCharacterFromTemplate = true;
					}
					return;
				}
				PlayerProfile val = new PlayerProfile(Game.instance.m_playerProfile.m_filename, (FileSource)0);
				if (!val.LoadPlayerProfileFromBytes(profileData) || Shared.CharacterNameIsForbidden(val.m_playerName))
				{
					Game.instance.Logout(true, true);
					ZNet.m_connectionStatus = (ConnectionStatus)5;
					connectionError = "The saved data on the server was corrupt, please contact your server admin or create a new character.";
					return;
				}
				serverCharacter = true;
				Game.instance.m_playerProfile = val;
				string characterSavePath = Utils.CharacterSavePath;
				char directorySeparatorChar = Path.DirectorySeparatorChar;
				string path = characterSavePath + directorySeparatorChar + Game.instance.m_playerProfile.m_filename + ".fch.signature";
				string characterSavePath2 = Utils.CharacterSavePath;
				directorySeparatorChar = Path.DirectorySeparatorChar;
				string text = characterSavePath2 + directorySeparatorChar + Game.instance.m_playerProfile.m_filename + ".fch.serverbackup";
				if (File.Exists(text) && File.Exists(path))
				{
					File.Delete(path);
					File.Delete(text);
					Utils.Log("Deleted emergency backup from " + text);
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private static class DetectBackupOnlyMode
		{
			[UsedImplicitly]
			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			private static void Postfix(ZNet __instance)
			{
				if (ServerCharacters.backupOnlyMode.GetToggle() && !__instance.IsServer())
				{
					serverCharacter = true;
				}
			}
		}

		[HarmonyPatch(typeof(Game), "SpawnPlayer")]
		private class InitializePlayerFromTemplate
		{
			[UsedImplicitly]
			private static void Postfix()
			{
				//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_021b: Unknown result type (might be due to invalid IL or missing references)
				bool num;
				if (!ServerCharacters.backupOnlyMode.GetToggle())
				{
					num = !acquireCharacterFromTemplate;
				}
				else
				{
					if (Game.instance.GetPlayerProfile().HaveLogoutPoint())
					{
						return;
					}
					num = Game.instance.GetPlayerProfile().HaveCustomSpawnPoint();
				}
				if (num)
				{
					return;
				}
				acquireCharacterFromTemplate = false;
				((Humanoid)Player.m_localPlayer).m_inventory.RemoveAll();
				Player.m_localPlayer.m_skills.m_skillData.Clear();
				Player.m_localPlayer.m_knownMaterial.Clear();
				Player.m_localPlayer.m_knownRecipes.Clear();
				Player.m_localPlayer.m_knownStations.Clear();
				Player.m_localPlayer.m_knownTexts.Clear();
				Player.m_localPlayer.m_uniques.Clear();
				Player.m_localPlayer.m_trophies.Clear();
				Player.m_localPlayer.m_customData.Clear();
				((Humanoid)Player.m_localPlayer).GiveDefaultItems();
				try
				{
					PlayerTemplate playerTemplate = new DeserializerBuilder().IgnoreFields().Build().Deserialize<PlayerTemplate>(ServerCharacters.playerTemplate.Value);
					if (playerTemplate != null)
					{
						foreach (KeyValuePair<string, float> skill in playerTemplate.skills)
						{
							((Character)Player.m_localPlayer).GetSkills().CheatRaiseSkill(skill.Key, skill.Value, true);
						}
						Inventory inventory = ((Humanoid)Player.m_localPlayer).m_inventory;
						foreach (KeyValuePair<string, int> item in playerTemplate.items)
						{
							inventory.AddItem(item.Key, item.Value, 1, 0, 0L, "", false);
						}
						List<PlayerTemplate.Position> spawn = playerTemplate.spawn;
						if (spawn != null && spawn.Count > 0)
						{
							State state = Random.state;
							Random.InitState(StringExtensionMethods.GetStableHashCode(UserInfo.GetLocalUser().NetworkUserId));
							int index = Random.Range(0, spawn.Count - 1);
							Random.state = state;
							((Component)Player.m_localPlayer).transform.position = new Vector3((float)spawn[index].x, (float)spawn[index].y, (float)spawn[index].z);
						}
					}
				}
				catch (SerializationException)
				{
				}
				Game.instance.SavePlayerProfile(true);
				if (!(ServerCharacters.firstLoginMessage.Value != ""))
				{
					return;
				}
				foreach (Player allPlayer in Player.GetAllPlayers())
				{
					((Character)allPlayer).Message((MessageType)2, ServerCharacters.firstLoginMessage.Value.Replace("{name}", ((Character)Player.m_localPlayer).GetHoverName()), 0, (Sprite)null);
				}
			}
		}

		[HarmonyPatch(typeof(Valkyrie), "Awake")]
		private class ChangeValkyrieTarget
		{
			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			private static void Prefix(Valkyrie __instance)
			{
				//IL_0049: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
				if (!((Component)__instance).GetComponent<ZNetView>().IsOwner())
				{
					return;
				}
				try
				{
					PlayerTemplate playerTemplate = new DeserializerBuilder().IgnoreFields().Build().Deserialize<PlayerTemplate>(ServerCharacters.playerTemplate.Value);
					if (playerTemplate != null)
					{
						List<PlayerTemplate.Position> spawn = playerTemplate.spawn;
						if (spawn != null && spawn.Count > 0)
						{
							State state = Random.state;
							Random.InitState(StringExtensionMethods.GetStableHashCode(UserInfo.GetLocalUser().NetworkUserId));
							int index = Random.Range(0, spawn.Count - 1);
							Random.state = state;
							((Component)Player.m_localPlayer).transform.position = new Vector3((float)spawn[index].x, (float)spawn[index].y, (float)spawn[index].z);
						}
					}
				}
				catch (SerializationException)
				{
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnSpawned")]
		private static class DisableValkyrieAndIntro
		{
			private static void Postfix(bool spawnValkyrie)
			{
				if (spawnValkyrie)
				{
					switch (ServerCharacters.newCharacterIntro.Value)
					{
					case Intro.Disabled:
						Game.instance.SkipIntro();
						break;
					case Intro.Valkyrie:
						Game.instance.m_inIntro = (Game.instance.m_queuedIntro = false);
						TextViewer.instance.HideIntro();
						break;
					}
				}
			}
		}

		[HarmonyPatch(typeof(Game), "FindSpawnPoint")]
		private class ReplaceSpawnPoint
		{
			private static bool CheckCustomSpawnPoint(out Vector3 pos)
			{
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Unknown result type (might be due to invalid IL or missing references)
				//IL_009c: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					PlayerTemplate playerTemplate = new DeserializerBuilder().IgnoreFields().Build().Deserialize<PlayerTemplate>(ServerCharacters.playerTemplate.Value);
					if (playerTemplate == null)
					{
						pos = Vector3.zero;
						return false;
					}
					List<PlayerTemplate.Position> spawn = playerTemplate.spawn;
					if (spawn != null && spawn.Count > 0)
					{
						State state = Random.state;
						Random.InitState(StringExtensionMethods.GetStableHashCode(UserInfo.GetLocalUser().NetworkUserId));
						int index = Random.Range(0, spawn.Count - 1);
						Random.state = state;
						pos = new Vector3((float)spawn[index].x, (float)spawn[index].y, (float)spawn[index].z);
						return true;
					}
				}
				catch (SerializationException)
				{
				}
				pos = Vector3.zero;
				return false;
			}

			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> _instructions, ILGenerator ilg)
			{
				//IL_015b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0160: Unknown result type (might be due to invalid IL or missing references)
				//IL_016e: Expected O, but got Unknown
				//IL_0197: Unknown result type (might be due to invalid IL or missing references)
				//IL_019d: Expected O, but got Unknown
				//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b1: Expected O, but got Unknown
				MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZoneSystem), "GetLocationIcon", (Type[])null, (Type[])null);
				FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(Game), "m_StartLocation");
				List<CodeInstruction> list = _instructions.ToList();
				for (int i = 0; i < list.Count; i++)
				{
					if (i < list.Count - 4 && list[i].opcode == OpCodes.Callvirt && CodeInstructionExtensions.OperandIs(list[i], (MemberInfo)methodInfo) && list[i - 2].opcode == OpCodes.Ldfld && CodeInstructionExtensions.OperandIs(list[i - 2], (MemberInfo)fieldInfo))
					{
						int num = i;
						MethodInfo methodInfo2 = AccessTools.DeclaredPropertyGetter(typeof(ZoneSystem), "instance");
						while (list[num].opcode != OpCodes.Call || !CodeInstructionExtensions.OperandIs(list[num], (MemberInfo)methodInfo2))
						{
							num--;
						}
						CodeInstruction obj = list.Skip(i).SkipWhile((CodeInstruction instr) => instr.opcode.FlowControl != FlowControl.Cond_Branch).Skip(1)
							.First();
						Label label = ilg.DefineLabel();
						obj.labels.Add(label);
						List<Label> labels = list[num].labels;
						list.InsertRange(num, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[4]
						{
							new CodeInstruction(OpCodes.Nop, (object)null)
							{
								labels = new List<Label>(labels)
							},
							list[i - 1],
							new CodeInstruction(OpCodes.Call, (object)AccessTools.DeclaredMethod(typeof(ReplaceSpawnPoint), "CheckCustomSpawnPoint", (Type[])null, (Type[])null)),
							new CodeInstruction(OpCodes.Brtrue, (object)label)
						});
						labels.Clear();
						break;
					}
				}
				return list;
			}
		}

		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
		[HarmonyPatch(typeof(Player), "CreateTombStone")]
		private class PreserveItemsOnHardcoreModeDeath
		{
			[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(2)]
			private static ZPackage playerSave;

			[HarmonyPriority(700)]
			private static void Prefix(Player __instance)
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_001d: Expected O, but got Unknown
				if (ServerCharacters.hardcoreMode.GetToggle() && iDied)
				{
					playerSave = new ZPackage();
					__instance.Save(playerSave);
					playerSave.SetPos(0);
				}
			}

			[HarmonyPriority(100)]
			private static void Postfix(Player __instance)
			{
				if (playerSave != null && ServerCharacters.hardcoreMode.GetToggle() && iDied)
				{
					__instance.m_knownTexts.Clear();
					__instance.m_knownStations.Clear();
					__instance.Load(playerSave);
					playerSave = null;
				}
			}
		}

		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
		[HarmonyPatch(typeof(Game), "UpdateRespawn")]
		private static class ChangeSpawnShoutMessage
		{
			private static string ReplaceMessage(string original)
			{
				if (!(ServerCharacters.loginMessage.Value == "I have arrived!"))
				{
					return ServerCharacters.loginMessage.Value;
				}
				return original;
			}

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator ilg)
			{
				MethodInfo sendText = AccessTools.DeclaredMethod(typeof(Chat), "SendText", (Type[])null, (Type[])null);
				foreach (CodeInstruction instruction in instructions)
				{
					if (CodeInstructionExtensions.Calls(instruction, sendText))
					{
						Label skipLabel = ilg.DefineLabel();
						Label endLabel = ilg.DefineLabel();
						yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.DeclaredMethod(typeof(ChangeSpawnShoutMessage), "ReplaceMessage", (Type[])null, (Type[])null));
						yield return new CodeInstruction(OpCodes.Dup, (object)null);
						yield return new CodeInstruction(OpCodes.Ldstr, (object)"");
						yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.DeclaredMethod(typeof(string), "Equals", new Type[2]
						{
							typeof(string),
							typeof(string)
						}, (Type[])null));
						yield return new CodeInstruction(OpCodes.Brtrue, (object)skipLabel);
						yield return instruction;
						yield return new CodeInstruction(OpCodes.Br, (object)endLabel);
						yield return new CodeInstruction(OpCodes.Pop, (object)null)
						{
							labels = new List<Label> { skipLabel }
						};
						yield return new CodeInstruction(OpCodes.Pop, (object)null);
						yield return new CodeInstruction(OpCodes.Pop, (object)null);
						yield return new CodeInstruction(OpCodes.Nop, (object)null)
						{
							labels = new List<Label> { endLabel }
						};
					}
					else
					{
						yield return instruction;
					}
				}
			}
		}

		[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
		private class ShowConnectionError
		{
			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			private static void Postfix(FejdStartup __instance)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Invalid comparison between Unknown and I4
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Invalid comparison between Unknown and I4
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Invalid comparison between Unknown and I4
				if ((int)ZNet.GetConnectionStatus() == 987345987)
				{
					__instance.m_connectionFailedError.text = "Server is undergoing maintenance. Please try again later.";
				}
				if ((int)ZNet.GetConnectionStatus() == 498209834)
				{
					__instance.m_connectionFailedError.text = "Your character name contains illegal characters. Please choose a different name.";
				}
				if ((int)ZNet.GetConnectionStatus() == 845979243)
				{
					__instance.m_connectionFailedError.text = "You are not allowed to create more than one character on this server.";
				}
				if (__instance.m_connectionFailedPanel.activeSelf && connectionError != null)
				{
					TMP_Text connectionFailedError = __instance.m_connectionFailedError;
					connectionFailedError.text = connectionFailedError.text + "\n" + connectionError;
					connectionError = null;
				}
				if (iDied && ServerCharacters.hardcoreMode.GetToggle())
				{
					__instance.m_connectionFailedError.text = "You died on a hardcore server. You can continue to use your character in singleplayer, but will have to create a new one to connect to the server.";
					__instance.m_connectionFailedPanel.SetActive(true);
					iDied = false;
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_Disconnect")]
		private class PatchZNetRPC_Disconnect
		{
			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			[UsedImplicitly]
			private static void Prefix(ZNet __instance)
			{
				if (!__instance.IsServer() && serverCharacter)
				{
					forceSynchronousSaving = true;
					Game.instance.SavePlayerProfile(true);
					forceSynchronousSaving = false;
				}
			}
		}

		[HarmonyPatch(typeof(Game), "OnApplicationQuit")]
		private class PatchGameOnApplicationQuit
		{
			[UsedImplicitly]
			private static void Prefix()
			{
				forceSynchronousSaving = true;
			}
		}

		[HarmonyPatch(typeof(Menu), "QuitGame")]
		private static class ForceSaveOnQuit
		{
			private static void Prefix()
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Invalid comparison between Unknown and I4
				if ((int)ZNet.m_onlineBackend == 1)
				{
					ZNet.instance.m_haveStoped = false;
					forceSynchronousSaving = false;
					Game.instance.SavePlayerProfile(true);
				}
			}
		}

		[HarmonyPatch(typeof(Game), "SavePlayerProfile")]
		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
		private class ForceSavingPosition
		{
			private static bool originalValue;

			[UsedImplicitly]
			private static void Prefix(Game __instance, ref bool setLogoutPoint, out bool __state)
			{
				__state = ZNet.instance.m_haveStoped;
				if (__instance.m_shuttingDown)
				{
					ZNet.instance.m_haveStoped = true;
				}
				if (ZNet.m_world == null || __instance.m_playerProfile.HaveLogoutPoint())
				{
					originalValue = true;
					return;
				}
				originalValue = setLogoutPoint;
				setLogoutPoint = true;
			}

			[UsedImplicitly]
			private static void Postfix(Game __instance)
			{
				if (!originalValue)
				{
					__instance.m_playerProfile.ClearLoguoutPoint();
				}
			}

			[UsedImplicitly]
			private static void Finalizer(bool __state)
			{
				ZNet.instance.m_haveStoped = __state;
			}
		}

		[HarmonyPatch(typeof(Game), "Logout")]
		private class PatchGameLogout
		{
			[UsedImplicitly]
			private static void Prefix()
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Invalid comparison between Unknown and I4
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Invalid comparison between Unknown and I4
				doEmergencyBackup = (int)ZNet.GetConnectionStatus() != 1 && (int)ZNet.GetConnectionStatus() != 2 && !Game.instance.IsShuttingDown();
				if (doEmergencyBackup)
				{
					Utils.Log("Lost connection to the server. Preparing for emergency backup of profile data.");
				}
			}
		}

		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
		private class PlayerSnapshot
		{
			public List<ItemData> inventory;

			public Dictionary<string, int> knownStations;

			public Dictionary<string, string> knownTexts;
		}

		[HarmonyPatch(typeof(Inventory), "Changed")]
		private class PatchInventoryChanged
		{
			public static bool queuedThisFrame;

			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			private static void Prefix(Inventory __instance)
			{
				ZNetPeer serverPeer;
				if (!queuedThisFrame && __instance == ((Humanoid)(Player.m_localPlayer?)).m_inventory)
				{
					serverPeer = ZNet.instance.GetServerPeer();
					if (serverPeer != null)
					{
						queuedThisFrame = true;
						((MonoBehaviour)ZNet.instance).StartCoroutine(saveAsync());
					}
				}
				[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
				IEnumerator saveAsync()
				{
					yield return null;
					queuedThisFrame = false;
					ZPackage val = new ZPackage();
					__instance.Save(val);
					foreach (bool item in Shared.sendCompressedDataToPeer(serverPeer, "ServerCharacters PlayerInventory", val.GetArray()))
					{
						if (!item)
						{
							yield return null;
						}
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		private class KickPlayerOnDeath
		{
			private static void Prefix()
			{
				if (ServerCharacters.hardcoreMode.GetToggle())
				{
					iDied = true;
					((MonoBehaviour)Game.instance).Invoke("Logout", 1f);
				}
			}
		}

		[HarmonyPatch]
		private static class MonitorPlayerActivityCheck
		{
			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			private static IEnumerable<MethodInfo> TargetMethods()
			{
				return new MethodInfo[2]
				{
					AccessTools.DeclaredMethod(typeof(ZInput), "GetButton", (Type[])null, (Type[])null),
					AccessTools.DeclaredMethod(typeof(ZInput), "GetButtonDown", (Type[])null, (Type[])null)
				};
			}

			private static void Postfix(ref bool __result)
			{
				if (__result)
				{
					MonitorPlayerActivity.counter = 0;
				}
			}
		}

		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
		[HarmonyPatch(typeof(Player), "SetLocalPlayer")]
		private static class MonitorPlayerActivity
		{
			public static int counter;

			private static IEnumerator MeasureActivity()
			{
				while (true)
				{
					if (++counter >= ServerCharacters.afkKickTimer.Value && ServerCharacters.afkKickTimer.Value > 0 && !ZNet.m_isServer && (!ServerCharacters.configSync.IsAdmin || ServerCharacters.excludeAdminsFromAfkCheck.Value == Toggle.Off))
					{
						Game.instance.Logout(true, true);
						ZNet.m_connectionStatus = (ConnectionStatus)4;
						connectionError = "You have been logged out due to inactivity.";
						counter = 0;
					}
					yield return (object)new WaitForSeconds(60f);
				}
			}

			private static void Postfix(Player __instance)
			{
				PatchInventoryChanged.queuedThisFrame = false;
				((MonoBehaviour)__instance).StartCoroutine(MeasureActivity());
			}
		}

		[HarmonyPatch(typeof(Minimap), "Awake")]
		private static class DisplayMaintenanceCountdown
		{
			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			private static void Postfix(Minimap __instance)
			{
				//IL_0072: Unknown result type (might be due to invalid IL or missing references)
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_009c: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
				maintenanceCountdown = ((Component)Object.Instantiate<Transform>(__instance.m_smallRoot.transform.Find("small_biome"), __instance.m_smallRoot.transform)).GetComponent<TextMeshProUGUI>();
				((Object)maintenanceCountdown).name = "ServerCharacters Maintenance Timer";
				((TMP_Text)maintenanceCountdown).text = "";
				((TMP_Text)maintenanceCountdown).alignment = (TextAlignmentOptions)513;
				RectTransform component = ((Component)maintenanceCountdown).GetComponent<RectTransform>();
				component.anchorMin = new Vector2(0f, 1f);
				component.anchorMax = new Vector2(1f, 1f);
				component.pivot = new Vector2(0f, 0.5f);
				component.anchoredPosition = new Vector2(5f, 0f - component.anchoredPosition.y);
			}
		}

		public static bool serverCharacter;

		private static bool currentlySaving;

		private static bool forceSynchronousSaving;

		private static bool acquireCharacterFromTemplate;

		private static bool doEmergencyBackup;

		private static bool iDied;

		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(1)]
		public static TextMeshProUGUI maintenanceCountdown;

		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(2)]
		private static PlayerSnapshot playerSnapShotLast;

		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(2)]
		private static PlayerSnapshot playerSnapShotNew;

		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(2)]
		private static byte[] serverEncryptionKey;

		private static long serverEncryptionTime;

		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(2)]
		private static string connectionError;

		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(2)]
		private static TaskCompletionSource<Vector3> awaitingPos;

		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
		private static byte[] generateProfileSignature(byte[] profileData, byte[] key)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			byte[] array = SHA512.Create().ComputeHash(profileData);
			Aes aes = Aes.Create();
			aes.Key = key;
			MemoryStream memoryStream = new MemoryStream();
			CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
			cryptoStream.Write(array, 0, array.Length);
			cryptoStream.FlushFinalBlock();
			cryptoStream.Close();
			ZPackage val = new ZPackage();
			val.Write(memoryStream.ToArray());
			val.Write(aes.IV);
			val.Write(serverEncryptionTime);
			return val.GetArray();
		}

		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
		private static void receiveEncryptionKeyFromServer(ZRpc peerRpc, ZPackage keyPackage)
		{
			serverEncryptionKey = keyPackage.ReadByteArray();
			serverEncryptionTime = keyPackage.ReadLong();
		}

		public static void snapShotProfile()
		{
			ZNet instance = ZNet.instance;
			if (instance != null && !instance.IsServer() && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				if (ZNet.instance.GetServerPing() < 1.5f)
				{
					playerSnapShotLast = playerSnapShotNew;
				}
				playerSnapShotNew = new PlayerSnapshot
				{
					inventory = ((Humanoid)Player.m_localPlayer).GetInventory().m_inventory.Select((ItemData d) => d.Clone()).ToList(),
					knownStations = Player.m_localPlayer.m_knownStations.ToDictionary((KeyValuePair<string, int> t) => t.Key, (KeyValuePair<string, int> t) => t.Value),
					knownTexts = Player.m_localPlayer.m_knownTexts.ToDictionary((KeyValuePair<string, string> t) => t.Key, (KeyValuePair<string, string> t) => t.Value)
				};
			}
		}
	}
	public enum Toggle
	{
		On = 1,
		Off = 0
	}
	public enum Intro
	{
		Disabled,
		Valkyrie,
		ValkyrieAndIntro
	}
	[PublicAPI]
	[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
	[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
	public class PlayerTemplate
	{
		[PublicAPI]
		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(0)]
		public class Position
		{
			public int x { get; set; }

			public int y { get; set; }

			public int z { get; set; }
		}

		public Dictionary<string, float> skills { get; set; } = new Dictionary<string, float>();


		public Dictionary<string, int> items { get; set; } = new Dictionary<string, int>();


		public List<Position> spawn { get; set; } = new List<Position>();

	}
	[BepInPlugin("org.bepinex.plugins.servercharacters", "Server Characters", "1.4.14")]
	[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
	[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
	[BepInIncompatibility("org.bepinex.plugins.valheim_plus")]
	public class ServerCharacters : BaseUnityPlugin
	{
		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(0)]
		private class ConfigurationManagerAttributes
		{
			[UsedImplicitly]
			public bool? Browsable = false;
		}

		private const string ModName = "Server Characters";

		private const string ModVersion = "1.4.14";

		private const string ModGUID = "org.bepinex.plugins.servercharacters";

		public static ServerCharacters selfReference = null;

		private static readonly Harmony harmony = new Harmony("org.bepinex.plugins.servercharacters");

		private float fixedUpdateCount;

		public int tickCount = int.MaxValue;

		public static int monotonicCounter = 0;

		public const int MaintenanceDisconnectMagic = 987345987;

		public const int CharacterNameDisconnectMagic = 498209834;

		public const int SingleCharacterModeDisconnectMagic = 845979243;

		public static readonly ConfigSync configSync = new ConfigSync("org.bepinex.plugins.servercharacters")
		{
			DisplayName = "Server Characters",
			CurrentVersion = "1.4.14",
			MinimumRequiredVersion = "1.4.14"
		};

		private static ConfigEntry<Toggle> serverConfigLocked = null;

		public static ConfigEntry<Toggle> singleCharacterMode = null;

		public static ConfigEntry<Toggle> backupOnlyMode = null;

		public static ConfigEntry<Toggle> hardcoreMode = null;

		public static ConfigEntry<Toggle> maintenanceMode = null;

		private static ConfigEntry<int> maintenanceTimer = null;

		public static ConfigEntry<int> backupsToKeep = null;

		public static ConfigEntry<int> autoSaveInterval = null;

		public static ConfigEntry<int> afkKickTimer = null;

		public static ConfigEntry<Toggle> excludeAdminsFromAfkCheck = null;

		public static ConfigEntry<string> webhookURL = null;

		private static ConfigEntry<string> webhookUsernameMaintenance = null;

		private static ConfigEntry<string> maintenanceEnabledText = null;

		private static ConfigEntry<string> maintenanceFinishedText = null;

		private static ConfigEntry<string> maintenanceAbortedText = null;

		private static ConfigEntry<string> maintenanceStartedText = null;

		public static ConfigEntry<string> loginMessage = null;

		public static ConfigEntry<string> firstLoginMessage = null;

		public static ConfigEntry<Toggle> postFirstLoginToWebhook = null;

		public static ConfigEntry<string> webhookUsernameOther = null;

		public static ConfigEntry<string> serverKey = null;

		public static ConfigEntry<string> serverListenAddress = null;

		public static ConfigEntry<Intro> newCharacterIntro = null;

		public static ConfigEntry<Toggle> storePoison = null;

		public static readonly CustomSyncedValue<string> playerTemplate = new CustomSyncedValue<string>(configSync, "PlayerTemplate", readCharacterTemplate());

		public static ManualLogSource logger => ((BaseUnityPlugin)selfReference).Logger;

		private static string pluginDir => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

		private ConfigEntry<T> config<[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(2)] T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, description);
			configSync.AddConfigEntry<T>(val).SynchronizedConfig = synchronizedSetting;
			return val;
		}

		private ConfigEntry<T> config<[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(2)] T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}

		public void Awake()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Expected O, but got Unknown
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Expected O, but got Unknown
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Expected O, but got Unknown
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Expected O, but got Unknown
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Expected O, but got Unknown
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Expected O, but got Unknown
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Expected O, but got Unknown
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Expected O, but got Unknown
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Expected O, but got Unknown
			//IL_0344: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Expected O, but got Unknown
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Expected O, but got Unknown
			//IL_03a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ae: Expected O, but got Unknown
			//IL_03ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d5: Expected O, but got Unknown
			//IL_03fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0405: Expected O, but got Unknown
			//IL_042e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Expected O, but got Unknown
			//IL_0462: Unknown result type (might be due to invalid IL or missing references)
			//IL_046d: Expected O, but got Unknown
			//IL_0574: Unknown result type (might be due to invalid IL or missing references)
			//IL_0581: Expected O, but got Unknown
			selfReference = this;
			serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only.");
			configSync.AddLockingConfigEntry<Toggle>(serverConfigLocked);
			afkKickTimer = config("1 - General", "AFK Kick Timer", 0, new ConfigDescription("Automatically kicks players, if they haven't moved at all in the configured time. In minutes. 0 is disabled.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 30), Array.Empty<object>()));
			excludeAdminsFromAfkCheck = config("1 - General", "Exclude Admins from AFK check", Toggle.Off, new ConfigDescription("Can be used to exclude admins from the AFK kick timer.", (AcceptableValueBase)null, Array.Empty<object>()));
			webhookURL = config("1 - General", "Discord Webhook URL", "", new ConfigDescription("Discord API endpoint to announce maintenance.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}), synchronizedSetting: false);
			loginMessage = config("1 - General", "Login Message", "I have arrived!", new ConfigDescription("Message to shout on login. Leave empty to not shout anything.", (AcceptableValueBase)null, Array.Empty<object>()));
			webhookUsernameOther = config("1 - General", "Discord Username Other", "Server Characters", new ConfigDescription("Username to be used for non-maintenance related posts to Discord.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}), synchronizedSetting: false);
			hardcoreMode = config("2 - Save Files", "Hardcore mode", Toggle.Off, "If set to on, players will be kicked from the server and their save file on the server will be deleted, if they die.");
			singleCharacterMode = config("2 - Save Files", "Single Character Mode", Toggle.Off, "If set to on, each SteamID / Xbox ID can create one character only on this server. Has no effect for admins.");
			backupOnlyMode = config("2 - Save Files", "Backup only mode", Toggle.Off, "Enabling this will not enforce the server profile anymore. DO NOT ENABLE THIS IF YOU DON'T KNOW EXACTLY WHAT YOU ARE DOING!");
			backupsToKeep = config("2 - Save Files", "Number of backups to keep", 25, new ConfigDescription("Sets the number of backups that should be stored for each character.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>()));
			autoSaveInterval = config("2 - Save Files", "Auto save interval", 30, new ConfigDescription("Minutes between auto saves of characters and the world.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 120), Array.Empty<object>()));
			autoSaveInterval.SettingChanged += [<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(0)] (object _, EventArgs _) =>
			{
				Game.m_saveInterval = autoSaveInterval.Value * 60;
			};
			storePoison = config("2 - Save Files", "Store poison debuff", Toggle.On, new ConfigDescription("If on, poison debuffs are stored in the save file on logout and applied on login, to prevent users from logging out if they are poisoned, to clear the debuff.", (AcceptableValueBase)null, Array.Empty<object>()));
			maintenanceMode = config("3 - Maintenance", "Maintenance Mode", Toggle.Off, "If set to on, a timer will start. If the timer elapses, all non-admins will be disconnected, the world will be saved and only admins will be able to connect to the server, until maintenance mode is toggled to off.");
			maintenanceMode.SettingChanged += toggleMaintenanceMode;
			maintenanceTimer = config("3 - Maintenance", "Maintenance Timer", 300, new ConfigDescription("Time in seconds that has to pass, before the maintenance mode becomes active.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 1800), Array.Empty<object>()));
			webhookUsernameMaintenance = config("3 - Maintenance", "Discord Username Maintenance", "Maintenance Bot", new ConfigDescription("Username to be used for maintenance related posts to Discord.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}), synchronizedSetting: false);
			maintenanceEnabledText = config("3 - Maintenance", "Maintenance enabled text", "Maintenance mode enabled. All non-admins will be disconnected in {time}.", new ConfigDescription("Message to be posted to Discord, when the maintenance mode has been toggled to 'On'. Leave empty to not post anything. Use {time} for the time until the maintenance starts.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}), synchronizedSetting: false);
			maintenanceFinishedText = config("3 - Maintenance", "Maintenance finished text", "Maintenance has been disabled and the server is back online. Have fun!", new ConfigDescription("Message to be posted to Discord, when the maintenance mode has been toggled to 'Off'. Leave empty to not post anything.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}), synchronizedSetting: false);
			maintenanceAbortedText = config("3 - Maintenance", "Maintenance aborted text", "Maintenance has been aborted.", new ConfigDescription("Message to be posted to Discord, when the maintenance has been aborted. Leave empty to not post anything.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}), synchronizedSetting: false);
			maintenanceStartedText = config("3 - Maintenance", "Maintenance started text", "Maintenance has started and players will be unable to connect.", new ConfigDescription("Message to be posted to Discord, when the maintenance has begun. Leave empty to not post anything.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}), synchronizedSetting: false);
			firstLoginMessage = config("3 - First Login", "First Login Message", "A new player logged in for the first time: {name}", new ConfigDescription("Message to display if a player logs in for the very first time. Leave empty to not display anything.", (AcceptableValueBase)null, Array.Empty<object>()));
			newCharacterIntro = config("3 - First Login", "Intro", Intro.ValkyrieAndIntro, new ConfigDescription("Sets the kind of intro new characters will get.", (AcceptableValueBase)null, Array.Empty<object>()));
			postFirstLoginToWebhook = config("3 - First Login", "First Login Webhook", Toggle.Off, new ConfigDescription("If on, the first login message is posted to the webhook as well.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}), synchronizedSetting: false);
			serverKey = config("4 - Other", "Server key", "", new ConfigDescription("DO NOT TOUCH THIS! DO NOT SHARE THIS! Encryption key used for emergency profile backups. DO NOT SHARE THIS! DO NOT TOUCH THIS!", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}), synchronizedSetting: false);
			serverListenAddress = config("4 - Other", "Webinterface listen address", "127.0.0.1:5982", new ConfigDescription("The address the webinterface API should listen on. Clear this value, if you don't use the webinterface.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}), synchronizedSetting: false);
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			harmony.PatchAll(executingAssembly);
			FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, "maintenance");
			fileSystemWatcher.Created += maintenanceFileEvent;
			fileSystemWatcher.Deleted += maintenanceFileEvent;
			fileSystemWatcher.IncludeSubdirectories = true;
			fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher.EnableRaisingEvents = true;
			FileSystemWatcher fileSystemWatcher2 = new FileSystemWatcher(pluginDir, "CharacterTemplate.yml");
			fileSystemWatcher2.Created += templateFileEvent;
			fileSystemWatcher2.Changed += templateFileEvent;
			fileSystemWatcher2.Renamed += templateFileEvent;
			fileSystemWatcher2.Deleted += templateFileEvent;
			fileSystemWatcher2.IncludeSubdirectories = true;
			fileSystemWatcher2.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher2.EnableRaisingEvents = true;
			ServerSide.generateServerKey();
			harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(ServerCharacters), "Initialize", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		public static void Initialize()
		{
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			harmony.Unpatch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Awake", (Type[])null, (Type[])null), (HarmonyPatchType)2, harmony.Id);
			if (!Utility.IsNullOrWhiteSpace(serverListenAddress.Value))
			{
				WebInterfaceAPI.StartServer();
			}
			Directory.CreateDirectory(Utils.CharacterSavePath);
			string characterFolderPath = PlayerProfile.GetCharacterFolderPath((FileSource)3);
			string[] files;
			if (Directory.Exists(characterFolderPath))
			{
				files = Directory.GetFiles(characterFolderPath);
				for (int i = 0; i < files.Length; i++)
				{
					FileInfo fileInfo = new FileInfo(files[i]);
					if (Utils.IsServerCharactersFilePattern(fileInfo.Name) || fileInfo.Name == "backups")
					{
						string fullName = fileInfo.FullName;
						string characterSavePath = Utils.CharacterSavePath;
						char directorySeparatorChar = Path.DirectorySeparatorChar;
						Directory.Move(fullName, characterSavePath + directorySeparatorChar + fileInfo.Name);
					}
				}
			}
			files = Directory.GetFiles(Utils.CharacterSavePath);
			for (int i = 0; i < files.Length; i++)
			{
				FileInfo fileInfo2 = new FileInfo(files[i]);
				if (fileInfo2.Name.EndsWith(".fch", StringComparison.Ordinal) && Regex.IsMatch(fileInfo2.Name.Split(new char[1] { '_' })[0], "^\\d+$"))
				{
					string? directoryName = fileInfo2.DirectoryName;
					char directorySeparatorChar = Path.DirectorySeparatorChar;
					string destFileName = directoryName + directorySeparatorChar + "Steam_" + fileInfo2.Name;
					fileInfo2.MoveTo(destFileName);
				}
			}
			files = Directory.GetFiles(Utils.CharacterSavePath);
			for (int i = 0; i < files.Length; i++)
			{
				FileInfo fileInfo3 = new FileInfo(files[i]);
				if (Utils.IsServerCharactersFilePattern(fileInfo3.Name))
				{
					Utils.ProfileName key = default(Utils.ProfileName);
					string[] array = fileInfo3.Name.Split(new char[1] { '_' });
					key.id = array[0] + "_" + array[1];
					key.name = array[2].Split(new char[1] { '.' })[0];
					PlayerProfile val = new PlayerProfile(fileInfo3.Name.Replace(".fch", ""), (FileSource)1);
					val.Load();
					Utils.Cache.profiles[key] = val;
				}
			}
		}

		private static void maintenanceFileEvent(object s, EventArgs e)
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			Toggle toggle = (File.Exists(configPath + directorySeparatorChar + "maintenance") ? Toggle.On : Toggle.Off);
			SyncedConfigEntry<Toggle> syncedConfigEntry = ConfigSync.ConfigData<Toggle>(maintenanceMode);
			if (syncedConfigEntry.LocalBaseValue == null)
			{
				maintenanceMode.Value = toggle;
			}
			else
			{
				syncedConfigEntry.LocalBaseValue = toggle;
			}
		}

		private static void templateFileEvent(object s, EventArgs e)
		{
			playerTemplate.AssignLocalValue(readCharacterTemplate());
		}

		private static string readCharacterTemplate()
		{
			string text = pluginDir;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			string path = text + directorySeparatorChar + "CharacterTemplate.yml";
			if (!File.Exists(path))
			{
				return "";
			}
			return File.ReadAllText(path);
		}

		private void toggleMaintenanceMode(object s, EventArgs e)
		{
			if (maintenanceMode.GetToggle())
			{
				string text = maintenanceEnabledText.Value.Replace("{time}", Utils.getHumanFriendlyTime(maintenanceTimer.Value));
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null);
				}
				Utils.Log(text);
				tickCount = maintenanceTimer.Value;
				WebInterfaceAPI.SendMaintenanceMessage(new Maintenance
				{
					startTime = DateTimeOffset.Now.ToUnixTimeSeconds() + tickCount,
					maintenanceActive = false
				});
				if (configSync.IsSourceOfTruth)
				{
					string configPath = Paths.ConfigPath;
					char directorySeparatorChar = Path.DirectorySeparatorChar;
					File.Create(configPath + directorySeparatorChar + "maintenance");
					Utils.PostToDiscord(text, webhookUsernameMaintenance.Value);
				}
				return;
			}
			if (configSync.IsSourceOfTruth)
			{
				string configPath2 = Paths.ConfigPath;
				char directorySeparatorChar = Path.DirectorySeparatorChar;
				File.Delete(configPath2 + directorySeparatorChar + "maintenance");
				Utils.PostToDiscord((tickCount <= maintenanceTimer.Value) ? maintenanceAbortedText.Value : maintenanceFinishedText.Value, webhookUsernameMaintenance.Value);
				WebInterfaceAPI.SendMaintenanceMessage(new Maintenance
				{
					startTime = 0L,
					maintenanceActive = false
				});
			}
			if (tickCount <= maintenanceTimer.Value)
			{
				Player localPlayer2 = Player.m_localPlayer;
				if (localPlayer2 != null)
				{
					((Character)localPlayer2).Message((MessageType)2, "Maintenance aborted", 0, (Sprite)null);
				}
				Utils.Log("Maintenance aborted");
				tickCount = int.MaxValue;
			}
		}

		private void FixedUpdate()
		{
			fixedUpdateCount += Time.fixedDeltaTime;
			if ((double)fixedUpdateCount < 1.0)
			{
				return;
			}
			ZNet instance = ZNet.instance;
			if ((instance == null || !instance.IsServer()) && ClientSide.serverCharacter)
			{
				ClientSide.snapShotProfile();
			}
			if (tickCount <= 0)
			{
				ZNet instance2 = ZNet.instance;
				if (instance2 != null && instance2.IsServer())
				{
					foreach (ZNetPeer peer in ZNet.instance.GetPeers())
					{
						if (!ZNet.instance.ListContainsId(ZNet.instance.m_adminList, peer.m_rpc.GetSocket().GetHostName()))
						{
							ZNet.instance.InternalKick(peer);
							Utils.Log("Kicked non-admin client " + Utils.GetPlayerID(peer.m_rpc.GetSocket().GetHostName()) + ", reason: Maintenance started.");
						}
					}
					ZNet.instance.SaveWorldAndPlayerProfiles();
					Utils.Log("Maintenance started. World has been saved.");
					Utils.PostToDiscord(maintenanceStartedText.Value, webhookUsernameMaintenance.Value);
					WebInterfaceAPI.SendMaintenanceMessage(new Maintenance
					{
						startTime = DateTimeOffset.Now.ToUnixTimeSeconds(),
						maintenanceActive = true
					});
				}
				tickCount = int.MaxValue;
			}
			fixedUpdateCount -= 1f;
			tickCount--;
			monotonicCounter++;
			if (Object.op_Implicit((Object)(object)ClientSide.maintenanceCountdown))
			{
				((TMP_Text)ClientSide.maintenanceCountdown).text = ((!maintenanceMode.GetToggle()) ? "" : ((tickCount < 1073741823) ? ("Maintenance in: " + TimeSpan.FromSeconds(tickCount).ToString("c")) : "Maintenance active"));
			}
		}
	}
	[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
	[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
	public static class ServerSide
	{
		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class PatchZNetOnNewConnection
		{
			private static byte[] deriveKey(long time)
			{
				return new Rfc2898DeriveBytes(ServerCharacters.serverKey.Value, BitConverter.GetBytes(time), 1000).GetBytes(32);
			}

			[UsedImplicitly]
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				//IL_01af: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b5: Expected O, but got Unknown
				if (__instance.IsServer())
				{
					if (ServerCharacters.maintenanceMode.GetToggle() && !isAdmin(peer.m_rpc))
					{
						peer.m_rpc.Invoke("Error", new object[1] { 987345987 });
						Utils.Log("Non-admin client " + Utils.GetPlayerID(peer.m_rpc.GetSocket().GetHostName()) + " tried to connect during maintenance and got disconnected");
						__instance.Disconnect(peer);
					}
					peer.m_rpc.Register<ZPackage>("ServerCharacters PlayerProfile", Shared.receiveCompressedFromPeer(delegate(ZRpc peerRpc, byte[] profileData)
					{
						onReceivedProfile(peerRpc, profileData);
					}));
					peer.m_rpc.Register<ZPackage>("ServerCharacters CheckSignature", Shared.receiveCompressedFromPeer(onReceivedSignature));
					peer.m_rpc.Register<ZPackage>("ServerCharacters PlayerInventory", Shared.receiveCompressedFromPeer(onReceivedInventory));
					peer.m_rpc.Register<ZPackage>("ServerCharacters PlayerDied", Shared.receiveCompressedFromPeer(onPlayerDied));
					peer.m_rpc.Register<string, string, string>("ServerCharacters ResetSkill", (Action<ZRpc, string, string, string>)onResetSkill);
					peer.m_rpc.Register<string, int, string, string>("ServerCharacters RaiseSkill", (Method<string, int, string, string>)onRaiseSkill);
					peer.m_rpc.Register<string, string>("ServerCharacters GetPlayerPos", (Action<ZRpc, string, string>)onGetPlayerPos);
					peer.m_rpc.Register<string, int, string, string>("ServerCharacters GiveItem", (Method<string, int, string, string>)onGiveItem);
					peer.m_rpc.Register<string, string, Vector3>("ServerCharacters SendOwnPos", (Action<ZRpc, string, string, Vector3>)onSendOwnPos);
					long ticks = DateTime.Now.Ticks;
					byte[] array = deriveKey(ticks);
					ZPackage val = new ZPackage();
					val.Write(array);
					val.Write(ticks);
					peer.m_rpc.Invoke("ServerCharacters KeyExchange", new object[1] { val });
				}
			}

			private static void onPlayerDied(ZRpc peerRpc, byte[] profileData)
			{
				PlayerProfile val = onReceivedProfile(peerRpc, profileData);
				if (val != null)
				{
					backupProfile(val);
					string characterSavePath = Utils.CharacterSavePath;
					char directorySeparatorChar = Path.DirectorySeparatorChar;
					string text = characterSavePath + directorySeparatorChar + val.m_filename + ".fch";
					File.Delete(text + ".old");
					File.Move(text, text + ".old");
				}
			}

			[return: <b871943b-bae9-475c-9342-11e13c80a649>Nullable(2)]
			private static PlayerProfile onReceivedProfile(ZRpc peerRpc, byte[] profileData)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Expected O, but got Unknown
				//IL_00de: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e4: Expected O, but got Unknown
				PlayerProfile val = new PlayerProfile((string)null, (FileSource)1);
				if (!val.LoadPlayerProfileFromBytes(profileData))
				{
					Utils.Log("Encountered invalid data for bytes from ID " + Utils.GetPlayerID(peerRpc.m_socket.GetHostName()));
					return null;
				}
				if (Shared.CharacterNameIsForbidden(val.GetName()))
				{
					peerRpc.Invoke("Error", new object[1] { 498209834 });
					ZNet.instance.Disconnect(ZNet.instance.GetPeer(peerRpc));
					Utils.Log("Client " + Utils.GetPlayerID(peerRpc.m_socket.GetHostName()) + " tried to connect with a bad profile name '" + val.GetName() + "' and got disconnected");
					return null;
				}
				val = new PlayerProfile(Utils.GetPlayerID(peerRpc.m_socket.GetHostName()) + "_" + val.GetName().ToLower(), (FileSource)1);
				val.LoadPlayerProfileFromBytes(profileData);
				if (!File.Exists(val.GetPath()) && ServerCharacters.postFirstLoginToWebhook.Value == Toggle.On)
				{
					Utils.PostToDiscord(ServerCharacters.firstLoginMessage.Value.Replace("{name}", val.GetName()), ServerCharacters.webhookUsernameOther.Value);
				}
				val.SavePlayerToDisk();
				Utils.Log("Saved player profile data for " + val.m_filename);
				return val;
			}

			private static void onReceivedInventory(ZRpc peerRpc, byte[] inventoryData)
			{
				Inventories[Utils.ProfileName.fromPeer(ZNet.instance.GetPeer(peerRpc))] = inventoryData;
			}

			private static void onReceivedSignature(ZRpc peerRpc, byte[] signedProfile)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: Expected O, but got Unknown
				//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b4: Expected O, but got Unknown
				//IL_0196: Unknown result type (might be due to invalid IL or missing references)
				//IL_019b: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a7: Expected O, but got Unknown
				ZPackage val = new ZPackage(signedProfile);
				byte[] array = val.ReadByteArray();
				byte[] signature = val.ReadByteArray();
				long num = VerifySignature(array, signature);
				if (num <= 0)
				{
					Utils.Log("Client " + Utils.GetPlayerID(peerRpc.m_socket.GetHostName()) + " tried to restore an emergency backup, but signature was invalid. Skipping.");
					return;
				}
				PlayerProfile val2 = new PlayerProfile((string)null, (FileSource)0);
				if (!val2.LoadPlayerProfileFromBytes(array) || Shared.CharacterNameIsForbidden(val2.GetName()))
				{
					Utils.Log("Client " + Utils.GetPlayerID(peerRpc.m_socket.GetHostName()) + " tried to restore an emergency backup, but the profile data is corrupted.");
					return;
				}
				val2 = new PlayerProfile(Utils.GetPlayerID(peerRpc.m_socket.GetHostName()) + "_" + val2.GetName().ToLower(), (FileSource)1);
				val2.LoadPlayerProfileFromBytes(array);
				string characterSavePath = Utils.CharacterSavePath;
				char directorySeparatorChar = Path.DirectorySeparatorChar;
				FileInfo fileInfo = new FileInfo(characterSavePath + directorySeparatorChar + val2.m_filename + ".fch");
				if (!fileInfo.Exists)
				{
					Utils.Log("Client " + Utils.GetPlayerID(peerRpc.m_socket.GetHostName()) + " tried to restore an emergency backup for the character '" + val2.m_filename + "' that does not belong to this server. Skipping.");
					return;
				}
				DateTime lastWriteTime = fileInfo.LastWriteTime;
				if (new DateTime(num) <= lastWriteTime)
				{
					Utils.Log("Client " + Utils.GetPlayerID(peerRpc.m_socket.GetHostName()) + " tried to restore an old emergency backup. Skipping.");
					return;
				}
				if (!Inventories.TryGetValue(Utils.ProfileName.fromPeer(ZNet.instance.GetPeer(peerRpc)), out var value))
				{
					PlayerProfile val3 = new PlayerProfile(val2.m_filename, (FileSource)0);
					val3.LoadPlayerFromDisk();
					value = ReadInventoryFromProfile(val3);
				}
				PatchPlayerProfileInventory(val2, value);
				val2.SavePlayerToDisk();
				Utils.Log("Client " + Utils.GetPlayerID(peerRpc.m_socket.GetHostName()) + " succesfully restored an emergency backup for " + val2.m_filename + ".");
			}

			private static long VerifySignature(byte[] profileData, byte[] signature)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				byte[] first = SHA512.Create().ComputeHash(profileData);
				ZPackage val = new ZPackage(signature);
				byte[] buffer = val.ReadByteArray();
				byte[] iV = val.ReadByteArray();
				long num = val.ReadLong();
				byte[] key = deriveKey(num);
				Aes aes = Aes.Create();
				aes.Key = key;
				aes.IV = iV;
				CryptoStream cryptoStream = new CryptoStream(new MemoryStream(buffer), aes.CreateDecryptor(), CryptoStreamMode.Read);
				MemoryStream memoryStream = new MemoryStream();
				cryptoStream.CopyTo(memoryStream);
				byte[] second = memoryStream.ToArray();
				if (!first.SequenceEqual(second))
				{
					return 0L;
				}
				return num;
			}
		}

		[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(0)]
		[HarmonyPatch(typeof(ZNet), "Disconnect")]
		private class PatchZNetDisconnect
		{
			[<b78d7983-aa0e-4cb1-9a4f-9cec4af42667>NullableContext(1)]
			private static void Prefix(ZNetPeer peer)
			{
				//IL_005f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0065: Expected O, but got Unknown
				ZNet instance = ZNet.instance;
				if (instance != null && instance.IsServer() && peerProfileNameMap.TryGetValue(peer, out var value) && Inventories.TryGetValue(value, out var value2))
				{
					Inventories.Remove(value);
					PlayerProfile val = new PlayerProfile(value.id + "_" + value.name.ToLower(), (FileSource)1);
					if (val.LoadPlayerFromDisk())
					{
						string characterSavePath = Utils.CharacterSavePath;
						char directorySeparatorChar = Path.DirectorySeparatorChar;
						FileInfo fileInfo = new FileInfo(characterSavePath + directorySeparatorChar + val.GetFilename() + ".fch");
						DateTime lastWriteTime = fileInfo.LastWriteTime;
						PatchPlayerProfileInventory(val, value2);
						val.SavePlayerToDisk();
						File.SetLastWriteTime(fileInfo.FullName, lastWriteTime);
					}
				}
				peerProfileNameMap.Remove(peer);
			}
		}

		[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private class SendConfigsAfterLogin
		{
			[<b871943b-bae9-475c-9342-11e13c80a649>Nullable(0)]
			private class BufferingSocket : ISocket
			{
				public volatile bool finished;

				public volatile int versionMatchQueued = -1;

				public readonly List<ZPackage> Package = new List<ZPackage>();

				public readonly ISocket Original;

				public BufferingSocket(ISocket original)
				{
					Original = original;
				}

				public bool IsConnected()
				{
					return Original.IsConnected();
				}

				public ZPackage Recv()
				{
					return Original.Recv();
				}

				public int GetSendQueueSize()
				{
					return Original.GetSendQueueSize();
				}

				public int GetCurrentSendRate()
				{
					return Original.GetCurrentSendRate();
				}

				public bool IsHost()
				{
					return Original.IsHost();
				}

				public void Dispose()
				{
					Original.Dispose();
				}

				public bool GotNewData()
				{
					return Original.GotNewData();
				}

				public void Close()
				{
					Original.Close();
				}

				public string GetEndPointString()
				{
					return Original.GetEndPointString();
				}

				public void GetAndResetStats(out int totalSent, out int totalRecv)
				{
					Original.GetAndResetStats(ref totalSent, ref totalRecv);
				}

				public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec)
				{
					Original.GetConnectionQuality(ref localQuality, ref remoteQuality, ref ping, ref outByteSec, ref inByteSec);
				}

				public ISocket Accept()
				{
					return Original.Accept();
				}

				public int GetHostPort()
				{
					return Original.GetHostPort();
				}

				public bool Flush()
				{
					return Original.Flush();
				}

				public string GetHostName()
				{
					return Original.GetHostName();
				}

				public void VersionMatch()
				{
					if (finished)
					{
						Original.VersionMatch();
					}
					else
					{
						versionMatchQueued = Package.Count;
					}
				}

				public void Send(ZPackage pkg)
				{
					//IL_004c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0052: Expected O, but got Unknown
					int pos = pkg.GetPos();
					pkg.SetPos(0);
					int num = pkg.ReadInt();
					if ((num == StringExtensionMethods.GetStableHashCode("PeerInfo") || num == StringExtensionMethods.GetStableHashCode("RoutedRPC") || num == StringExtensionMethods.GetStableHashCode("ZDOData")) && !finished)
					{
						ZPackage val = new ZPackage(pkg.GetArray());
						val.SetPos(pos);
						Package.Add(val);
					}
					else
					{
						pkg.SetPos(pos);
						Original.Send(pkg);
					}
				}
			}

			[HarmonyPriority(800)]
			private static void Prefix([<b871943b-bae9-475c-9342-11e13c80a649>Nullable(2)] ref BufferingSocket __