// // /*===============================================================================
// // 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