A game about forced loneliness, made by TACStudios
at master 278 lines 11 kB view raw
1using System; 2using System.Collections.Generic; 3using Unity.Multiplayer.Center.Questionnaire; 4 5namespace Unity.Multiplayer.Center.Recommendations 6{ 7 /// <summary> 8 /// The source of data for the recommender system. Based on scoring and this data, the recommender system will 9 /// populate the recommendation view data. 10 /// </summary> 11 [Serializable] 12 internal class RecommenderSystemData 13 { 14 /// <summary> 15 /// The Unity version for which this recommendation data is valid. 16 /// </summary> 17 public string TargetUnityVersion; 18 19 /// <summary> 20 /// Stores all the recommended solutions. This is serialized. 21 /// </summary> 22 public RecommendedSolution[] RecommendedSolutions; 23 24 /// <summary> 25 /// Stores all the package details. 26 /// This is serialized. 27 /// </summary> 28 public PackageDetails[] Packages; 29 30 /// <summary> 31 /// Provides convenient access to the package details by package id. 32 /// </summary> 33 public Dictionary<string, PackageDetails> PackageDetailsById 34 { 35 get 36 { 37 if (m_PackageDetailsById != null) return m_PackageDetailsById; 38 m_PackageDetailsById = Utils.ToDictionary(Packages, p => p.Id); 39 return m_PackageDetailsById; 40 } 41 } 42 43 public Dictionary<PossibleSolution, RecommendedSolution> SolutionsByType 44 { 45 get 46 { 47 if (m_SolutionsByType != null) return m_SolutionsByType; 48 m_SolutionsByType = Utils.ToDictionary(RecommendedSolutions, s => s.Type); 49 return m_SolutionsByType; 50 } 51 } 52 53 /// <summary> 54 /// Checks for incompatibility between the netcode and hosting model. 55 /// </summary> 56 /// <param name="netcode">The netcode type</param> 57 /// <param name="hostingModel">The hosting model</param> 58 /// <param name="reason">The reason for the incompatibility, filled when this function returns false.</param> 59 /// <returns>True if compatible (default), False otherwise</returns> 60 public bool IsHostingModelCompatibleWithNetcode(PossibleSolution netcode, PossibleSolution hostingModel, out string reason) 61 { 62 m_IncompatibleHostingModels ??= Utils.ToDictionary(RecommendedSolutions); 63 return !m_IncompatibleHostingModels.TryGetValue(new SolutionSelection(netcode, hostingModel), out reason); 64 } 65 66 /// <summary> 67 /// Patch incompatibility values. 68 /// </summary> 69 /// <param name="netcode">Netcode solution</param> 70 /// <param name="hostingModel">Hosting model solution</param> 71 /// <param name="newIsCompatible">Whether we should now consider the two solutions compatible</param> 72 /// <param name="reason">If incompatible, why it is incompatible.</param> 73 internal void UpdateIncompatibility(PossibleSolution netcode, PossibleSolution hostingModel, bool newIsCompatible, string reason=null) 74 { 75 Utils.UpdateIncompatibilityInSolutions(RecommendedSolutions, netcode, hostingModel, newIsCompatible, reason); 76 m_IncompatibleHostingModels = Utils.ToDictionary(RecommendedSolutions); 77 m_SolutionsByType = Utils.ToDictionary(RecommendedSolutions, s => s.Type); 78 } 79 80 Dictionary<string, PackageDetails> m_PackageDetailsById; 81 Dictionary<PossibleSolution, RecommendedSolution> m_SolutionsByType; 82 Dictionary<SolutionSelection, string> m_IncompatibleHostingModels; 83 } 84 85 [Serializable] 86 internal struct SolutionSelection 87 { 88 public PossibleSolution Netcode; 89 public PossibleSolution HostingModel; 90 public SolutionSelection(PossibleSolution netcode, PossibleSolution hostingModel) 91 { 92 Netcode = netcode; 93 HostingModel = hostingModel; 94 } 95 } 96 97 /// <summary> 98 /// A possible solution and whether packages are recommended or not 99 /// </summary> 100 [Serializable] 101 internal class RecommendedSolution 102 { 103 /// <summary> 104 /// The type of solution 105 /// </summary> 106 public PossibleSolution Type; 107 108 /// <summary> 109 /// The name of the solution as shown in the UI. 110 /// </summary> 111 public string Title; 112 113 /// <summary> 114 /// Optional package ID associated with that solution (e.g. a netcode package or the cloud code package). 115 /// Use this field if the package has to mandatorily be installed when the solution is selected. 116 /// </summary> 117 public string MainPackageId;// only id because scoring will impact the rest 118 119 /// <summary> 120 /// Url to documentation describing the solution. 121 /// </summary> 122 public string DocUrl; 123 124 /// <summary> 125 /// Short description of the solution. 126 /// </summary> 127 public string ShortDescription; 128 129 /// <summary> 130 /// The packages and the associated recommendation type. 131 /// If the Type is a netcode Type, all the possible packages should be in this array. 132 /// If the Type is a hosting model, this will contain only overrides in case a package is incompatible or 133 /// featured for the hosting model. 134 /// </summary> 135 public RecommendedPackage[] RecommendedPackages; 136 137 /// <summary> 138 /// Solutions that are incompatible with this solution. 139 /// Typically used for netcode solutions. 140 /// </summary> 141 public IncompatibleSolution[] IncompatibleSolutions = Array.Empty<IncompatibleSolution>(); 142 } 143 144 /// <summary> 145 /// Stores why a solution is incompatible with something and why. 146 /// </summary> 147 [Serializable] 148 internal class IncompatibleSolution 149 { 150 /// <summary> 151 /// What is incompatible. 152 /// </summary> 153 public PossibleSolution Solution; 154 155 /// <summary> 156 /// Why it is incompatible. 157 /// </summary> 158 public string Reason; 159 160 public IncompatibleSolution(PossibleSolution solution, string reason) 161 { 162 Solution = solution; 163 Reason = reason; 164 } 165 } 166 167 /// <summary> 168 /// A package, whether it is recommended or not (context dependent), and why. 169 /// </summary> 170 [Serializable] 171 internal class RecommendedPackage 172 { 173 /// <summary> 174 /// The package id (e.g. com.unity.netcode) 175 /// </summary> 176 public string PackageId; 177 178 /// <summary> 179 /// Whether it is recommended or not. 180 /// </summary> 181 public RecommendationType Type; 182 183 /// <summary> 184 /// Why it is recommended or not. 185 /// </summary> 186 public string Reason; 187 188 public RecommendedPackage(string packageId, RecommendationType type, string reason) 189 { 190 PackageId = packageId; 191 Type = type; 192 Reason = reason; 193 } 194 } 195 196 [Serializable] 197 internal class PackageDetails 198 { 199 public string Id; 200 public string Name; 201 public string ShortDescription; 202 public string DocsUrl; 203 public string[] AdditionalPackages; 204 205 /// <summary> 206 /// In case we want to promote a specific pre-release version, this is set (by default, this is null 207 /// and the default package manager version is used). 208 /// </summary> 209 public string PreReleaseVersion; 210 211 /// <summary> 212 /// Details about the package. 213 /// </summary> 214 /// <param name="id">Package ID</param> 215 /// <param name="name">Package Name (for display)</param> 216 /// <param name="shortDescription">Short description.</param> 217 /// <param name="docsUrl">Link to Docs</param> 218 /// <param name="additionalPackages">Ids of packages that should be installed along this one, but are not formal dependencies.</param> 219 public PackageDetails(string id, string name, string shortDescription, string docsUrl, string[] additionalPackages = null) 220 { 221 Id = id; 222 Name = name; 223 ShortDescription = shortDescription; 224 DocsUrl = docsUrl; 225 AdditionalPackages = additionalPackages; 226 } 227 } 228 229 static class Utils 230 { 231 public static Dictionary<TKey, T> ToDictionary<T, TKey>(T[] array, Func<T, TKey> keySelector) 232 { 233 if (array == null) return null; 234 var result = new Dictionary<TKey, T>(); 235 foreach (var item in array) 236 { 237 result[keySelector(item)] = item; 238 } 239 240 return result; 241 } 242 243 public static Dictionary<SolutionSelection, string> ToDictionary(RecommendedSolution[] solutions) 244 { 245 var result = new Dictionary<SolutionSelection, string>(); 246 foreach (var recommendedSolution in solutions) 247 { 248 foreach (var incompatibleHostingModel in recommendedSolution.IncompatibleSolutions) 249 { 250 var key = new SolutionSelection(recommendedSolution.Type, incompatibleHostingModel.Solution); 251 result.Add(key, incompatibleHostingModel.Reason); 252 } 253 } 254 255 return result; 256 } 257 258 public static void UpdateIncompatibilityInSolutions(RecommendedSolution[] solutions, PossibleSolution netcode, 259 PossibleSolution hostingModel, bool newIsCompatible, string reason) 260 { 261 foreach (var recommendedSolution in solutions) 262 { 263 if (recommendedSolution.Type != netcode) continue; 264 265 var incompatibleSolution = Array.Find(recommendedSolution.IncompatibleSolutions, s => s.Solution == hostingModel); 266 if (newIsCompatible && incompatibleSolution != null) 267 { 268 recommendedSolution.IncompatibleSolutions = Array.FindAll(recommendedSolution.IncompatibleSolutions, s => s.Solution != hostingModel); 269 } 270 else if (!newIsCompatible && incompatibleSolution == null) 271 { 272 Array.Resize(ref recommendedSolution.IncompatibleSolutions, recommendedSolution.IncompatibleSolutions.Length + 1); 273 recommendedSolution.IncompatibleSolutions[^1] = new IncompatibleSolution(hostingModel, reason); 274 } 275 } 276 } 277 } 278}