1
0
mirror of synced 2025-02-17 11:18:31 +01:00

Generate deterministic UIDs for official songs + TJA, DLC compatibility

This commit is contained in:
goaaats 2022-02-07 13:11:27 +01:00
parent f65641ac5f
commit 25b558cdb9
No known key found for this signature in database
GPG Key ID: 49E2AA8C6A76498B
3 changed files with 107 additions and 97 deletions

85
TakoTako/MurmurHash2.cs Normal file
View File

@ -0,0 +1,85 @@
using System.Text;
// MIT License
//
// Copyright (c) 2017 Jitbit, 2022 TaikoMods contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
namespace TaikoMods;
public class MurmurHash2
{
public static uint Hash(string data)
{
return Hash(Encoding.UTF8.GetBytes(data));
}
public static uint Hash(byte[] data)
{
return Hash(data, 0xc58f1a7a);
}
private const uint m = 0x5bd1e995;
private const int r = 24;
public static uint Hash(byte[] data, uint seed)
{
var length = data.Length;
if (length == 0)
return 0;
var h = seed ^ (uint)length;
var currentIndex = 0;
while (length >= 4)
{
var k = (uint)(data[currentIndex++] | (data[currentIndex++] << 8) | (data[currentIndex++] << 16) |
(data[currentIndex++] << 24));
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
length -= 4;
}
switch (length)
{
case 3:
h ^= (ushort)(data[currentIndex++] | (data[currentIndex++] << 8));
h ^= (uint)(data[currentIndex] << 16);
h *= m;
break;
case 2:
h ^= (ushort)(data[currentIndex++] | (data[currentIndex] << 8));
h *= m;
break;
case 1:
h ^= data[currentIndex];
h *= m;
break;
}
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
}

View File

@ -15,6 +15,7 @@ using System.Threading.Tasks;
using BepInEx.Logging; using BepInEx.Logging;
using HarmonyLib; using HarmonyLib;
using Newtonsoft.Json; using Newtonsoft.Json;
using TaikoMods;
using TakoTako.Common; using TakoTako.Common;
using Unity.Collections; using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe; using Unity.Collections.LowLevel.Unsafe;
@ -372,24 +373,32 @@ public class MusicPatch
song.genreNo = 7; song.genreNo = 7;
} }
var instanceId = Guid.NewGuid().ToString(); if (isTjaSong)
{
song.id = song.songName?.text + song.songSubtitle?.text + song.songDetail?.text;
if (string.IsNullOrEmpty(song.id))
throw new Exception($"Song at {directory} does not have name, subtitle or detail text");
song.id += song.fumenOffsetPos + song.previewPos;
}
song.SongName = song.id; song.SongName = song.id;
song.FolderPath = directory; song.FolderPath = directory;
song.id = instanceId;
if (uniqueIdToSong.ContainsKey(song.uniqueId) || (song.uniqueId >= 0 && song.uniqueId <= SaveDataMax)) // Clip off the last bit of the hash to make sure that the number is positive. This will lead to more collisions, but we should be fine.
song.uniqueId = (int)(MurmurHash2.Hash(song.id) & 0xFFFF_FFF);
if (song.uniqueId <= SaveDataMax)
song.uniqueId += SaveDataMax;
if (uniqueIdToSong.ContainsKey(song.uniqueId))
{ {
var uniqueIdTest = unchecked(song.id.GetHashCode() + song.previewPos + song.fumenOffsetPos); throw new Exception($"Song \"{song.id}\" has collision with \"{uniqueIdToSong[song.uniqueId].id}\", bailing out...");
while (uniqueIdToSong.ContainsKey(uniqueIdTest) || (uniqueIdTest >= 0 && uniqueIdTest <= SaveDataMax))
uniqueIdTest = unchecked((uniqueIdTest + 1) * (uniqueIdTest + 1));
song.uniqueId = uniqueIdTest;
} }
customSongsList.Add(song); customSongsList.Add(song);
idToSong[song.id] = song; idToSong[song.id] = song;
uniqueIdToSong[song.uniqueId] = song; uniqueIdToSong[song.uniqueId] = song;
Log.LogInfo($"Added {(isTjaSong ? "TJA" : "")} Song {song.songName.text}"); Log.LogInfo($"Added{(isTjaSong ? " TJA" : "")} Song {song.songName.text}({song.uniqueId})");
} }
} }
@ -456,10 +465,10 @@ public class MusicPatch
$"song_{song.id}", $"song_{song.id}",
song.order, song.order,
song.genreNo, song.genreNo,
!Plugin.Instance.ConfigDisableCustomDLCSongs.Value, true, // We always want to mark songs as DLC, otherwise ranked games will be broken as you are gonna match songs that other people don't have
false, false,
0, false, 0, false,
0, 2, // Always mark custom songs as "both players need to have this song to play it"
new[] new[]
{ {
song.branchEasy, song.branchEasy,
@ -1287,83 +1296,6 @@ public class MusicPatch
#pragma warning restore Harmony003 #pragma warning restore Harmony003
} }
/// <summary>
/// Allow for a song id less than 0
/// </summary>
[HarmonyPatch(typeof(EnsoDataManager), "DecideSetting")]
[HarmonyPrefix]
public static bool DecideSetting_Prefix(EnsoDataManager __instance)
{
var ensoSettings = (EnsoData.Settings) typeof(EnsoDataManager).GetField("ensoSettings", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
// if (ensoSettings.musicuid.Length <= 0)
// {
// MusicDataInterface.MusicInfoAccesser infoByUniqueId = TaikoSingletonMonoBehaviour<CommonObjects>.Instance.MyDataManager.MusicData.GetInfoByUniqueId(ensoSettings.musicUniqueId);
// if (infoByUniqueId != null)
// {
// ensoSettings.musicuid = infoByUniqueId.Id;
// }
// }
// else if (ensoSettings.musicUniqueId <= DataConst.InvalidId)
// {
// MusicDataInterface.MusicInfoAccesser infoById = TaikoSingletonMonoBehaviour<CommonObjects>.Instance.MyDataManager.MusicData.GetInfoById(ensoSettings.musicuid);
// if (infoById != null)
// {
// ensoSettings.musicUniqueId = infoById.UniqueId;
// }
// }
if (ensoSettings.musicuid.Length <= 0 /* || ensoSettings.musicUniqueId <= DataConst.InvalidId*/)
{
List<MusicDataInterface.MusicInfoAccesser> musicInfoAccessers = TaikoSingletonMonoBehaviour<CommonObjects>.Instance.MyDataManager.MusicData.musicInfoAccessers;
for (int i = 0; i < musicInfoAccessers.Count; i++)
{
if (!musicInfoAccessers[i].Debug)
{
ensoSettings.musicuid = musicInfoAccessers[i].Id;
ensoSettings.musicUniqueId = musicInfoAccessers[i].UniqueId;
}
}
}
MusicDataInterface.MusicInfoAccesser infoByUniqueId2 = TaikoSingletonMonoBehaviour<CommonObjects>.Instance.MyDataManager.MusicData.GetInfoByUniqueId(ensoSettings.musicUniqueId);
if (infoByUniqueId2 != null)
{
ensoSettings.songFilePath = infoByUniqueId2.SongFileName;
}
__instance.DecidePartsSetting();
if (ensoSettings.ensoType == EnsoData.EnsoType.Normal)
{
int num = 0;
int dlcType = 2;
if (ensoSettings.rankMatchType == EnsoData.RankMatchType.None)
{
num = ((ensoSettings.playerNum != 1) ? 1 : 0);
}
else if (ensoSettings.rankMatchType == EnsoData.RankMatchType.RankMatch)
{
num = 2;
ensoSettings.isRandomSelect = false;
ensoSettings.isDailyBonus = false;
}
else
{
num = 3;
ensoSettings.isRandomSelect = false;
ensoSettings.isDailyBonus = false;
}
TaikoSingletonMonoBehaviour<CommonObjects>.Instance.CosmosLib._kpiListCommon._musicKpiInfo.SetMusicSortSettings(num, dlcType, ensoSettings.isRandomSelect, ensoSettings.isDailyBonus);
}
else
{
ensoSettings.isRandomSelect = false;
ensoSettings.isDailyBonus = false;
}
typeof(EnsoDataManager).GetField("ensoSettings", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, ensoSettings);
return false;
}
#endregion #endregion
#region Read Fumen #region Read Fumen
@ -1950,4 +1882,4 @@ public class MusicPatch
public string FolderPath; public string FolderPath;
public string SongName; public string SongName;
} }
} }

View File

@ -20,7 +20,6 @@ namespace TakoTako
public ConfigEntry<string> ConfigSongDirectory; public ConfigEntry<string> ConfigSongDirectory;
public ConfigEntry<bool> ConfigSaveEnabled; public ConfigEntry<bool> ConfigSaveEnabled;
public ConfigEntry<string> ConfigSaveDirectory; public ConfigEntry<string> ConfigSaveDirectory;
public ConfigEntry<bool> ConfigDisableCustomDLCSongs;
public ConfigEntry<string> ConfigOverrideDefaultSongLanguage; public ConfigEntry<string> ConfigOverrideDefaultSongLanguage;
public ConfigEntry<bool> ConfigApplyGenreOverride; public ConfigEntry<bool> ConfigApplyGenreOverride;
@ -64,12 +63,6 @@ namespace TakoTako
$"{userFolder}/Documents/{typeof(Plugin).Namespace}/saves", $"{userFolder}/Documents/{typeof(Plugin).Namespace}/saves",
"The directory where saves are stored"); "The directory where saves are stored");
ConfigDisableCustomDLCSongs = Config.Bind("CustomSongs",
"DisableCustomDLCSongs",
false,
"By default, DLC is enabled for custom songs, this is to reduce any hiccups when playing online with other people. " +
"Set this to true if you want DLC to be marked as false, be aware that the fact you're playing a custom song will be sent over the internet");
ConfigOverrideDefaultSongLanguage = Config.Bind("CustomSongs", ConfigOverrideDefaultSongLanguage = Config.Bind("CustomSongs",
"ConfigOverrideDefaultSongLanguage", "ConfigOverrideDefaultSongLanguage",
string.Empty, string.Empty,
@ -124,4 +117,4 @@ namespace TakoTako
StartCoroutine(enumerator); StartCoroutine(enumerator);
} }
} }
} }