// // /*=============================================================================== // // Copyright (C) 2024 PhantomsXR Ltd. All Rights Reserved. // // // // This file is part of the Phantom.XRMOD.PhotonModule.Runtime. // // // // The AVPPlatform cannot be copied, distributed, or made available to // // third-parties for commercial purposes without written permission of PhantomsXR Ltd. // // // // Contact info@phantomsxr.com for licensing requests. // // ===============================================================================*/ #if FUSION2 using System; using System.Security.Cryptography; using System.Text; using Fusion; using UnityEngine; namespace Phantom.XRMOD.PhotonModule.Runtime { public class PartyCodeGenerator { /// /// Available characters for the code generation. The default setup skips O and 0 for example. /// [InlineHelp] public string ValidCharacters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"; /// /// The length of the party code. /// [InlineHelp, Range(1, 32)] public int Length = 8; /// /// The position of the encoded region. /// [InlineHelp, Range(1, 32)] public int EncodedRegionPosition = 4; /// /// Create a random party code with default length. /// /// Random code public virtual string Create() { return Create(Length); } /// /// Create a party code with variable length. /// /// Code length /// Random code public virtual string Create(int _length) { return Create(_length, ValidCharacters); } /// /// Creates a random party code. /// /// Code length /// Useable characters /// Random code public static string Create(int _length, string _validCharacters) { _length = Math.Max(1, Math.Min(_length, 128)); // m = 238 = highest multiple of 34 in 255 var m = Mathf.FloorToInt((255.0f / _validCharacters.Length)) * _validCharacters.Length; if (m <= 0) { Debug.LogError($"Number of valid character ({_validCharacters.Length}) has to be less than 255."); return null; } var res = new StringBuilder(); using (RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider()) { while (res.Length != _length) { var bytes = new byte[8]; provider.GetBytes(bytes); foreach (var b in bytes) { if (b >= m || res.Length == _length) continue; var character = _validCharacters[b % _validCharacters.Length]; res.Append(character); } } } return res.ToString(); } /// /// Checks if a party code is valid. /// /// Code /// True, if the party code consists of the desired length and characters public virtual bool IsValid(string _code) { return IsValid(_code, Length); } /// /// Checks if a party code is valid. /// /// Code /// Variable length /// True, if the party code consists of the desired length and characters public virtual bool IsValid(string _code, int _length) { if (string.IsNullOrEmpty(_code)) { return false; } if (_code.Length != Length) { return false; } for (int i = 0; i < _code.Length; i++) { if (ValidCharacters.Contains(_code[i]) == false) { return false; } } return true; } /// /// Substitutes one character with the region. /// /// Code /// Region index /// New code public virtual string EncodeRegion(string _code, int _region) { if (string.IsNullOrEmpty(_code)) { return null; } if (_region < 0 || _region >= 32) { return null; } if (_region >= ValidCharacters.Length) { return null; } var index = Math.Clamp(EncodedRegionPosition, 0, _code.Length - 1); if (index < 0 || index >= _code.Length) { return null; } return _code.Remove(index, 1).Insert(index, ValidCharacters[_region].ToString()); } /// /// Reads the characater at the position as a int. /// /// Code /// Region index public virtual int DecodeRegion(string code) { if (string.IsNullOrEmpty(code)) { return -1; } var index = Math.Clamp(EncodedRegionPosition, 0, code.Length - 1); if (index < 0 || index >= code.Length) { return -1; } return ValidCharacters.IndexOf(code[index]); } } } #endif