프로젝트 세팅 중

This commit is contained in:
Ino 2026-01-08 06:27:42 +09:00
parent b0a7d91e8e
commit a703835aef
1652 changed files with 177961 additions and 0 deletions

8
Assets/Editor.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 998de5b9fe51f3848967851e68354d90
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

378
Assets/Editor/AutoBuild.cs Normal file
View File

@ -0,0 +1,378 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Callbacks;
#if UNITY_ANDROID
#elif UNITY_IOS
using UnityEditor.iOS.Xcode;
using UnityEditor.iOS.Xcode.Extensions;
using Facebook.Unity.Settings;
#endif
using UnityEngine;
public static class AutoBuild
{
static string path_dev = "AndroidData/mysticatshop_Test_";
static string path_dev_onestore = "AndroidData/mysticatshop_Test_OneStore_";
static string path_live = "AndroidData/mysticatshop_Live_";
static string path_live_onestore = "AndroidData/mysticatshop_Live_OneStore";
// 툴씬 이름 목록
private static readonly string[] toolScenes =
{
"96_Tool_MobScale",
"97_Tool_Effect",
"98_Tool_Mob",
"99_Tool"
};
private static List<EditorBuildSettingsScene> backupScenes;
static void Common(BuildTargetGroup _btg, BuildTarget _bt)
{
// 씬 빼기
backupScenes = EditorBuildSettings.scenes.ToList();
var filtered = backupScenes
.Where(scene => !toolScenes.Any(tool => scene.path.Contains(tool)))
.ToList();
EditorBuildSettings.scenes = filtered.ToArray();
PlayerSettings.applicationIdentifier = "com.nerdnavis.mysticatshop";
EditorUserBuildSettings.SwitchActiveBuildTarget(_btg, _bt);
if (_bt == BuildTarget.Android)
{
PlayerSettings.Android.splitApplicationBinary = false;
File.WriteAllText("Assets/Resources/VersionCode.txt", PlayerSettings.Android.bundleVersionCode.ToString());
PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7 | AndroidArchitecture.ARM64;
}
else
{
File.WriteAllText("Assets/Resources/VersionCode.txt", PlayerSettings.iOS.buildNumber);
PlayerSettings.iOS.appleDeveloperTeamID = "788PYWMPC6"; // 필굿밴디츠
//PlayerSettings.iOS.appleDeveloperTeamID = "A9997B8HR5"; // 나인탭
PlayerSettings.iOS.appleEnableAutomaticSigning = true;
PlayerSettings.iOS.sdkVersion = iOSSdkVersion.DeviceSDK;
}
}
static void Common_End()
{
// 씬 넣기
if (backupScenes != null)
{
EditorBuildSettings.scenes = backupScenes.ToArray();
}
EmptySymbol();
}
static void Show_BuildFolder(BuildTarget _bt)
{
if (_bt == BuildTarget.Android) System.Diagnostics.Process.Start("AndroidData");
else System.Diagnostics.Process.Start("IOSData");
}
static void BuildStart(string path, BuildTarget _bt)
{
BuildPipeline.BuildPlayer(EditorBuildSettings.scenes, path, _bt, BuildOptions.None);
Show_BuildFolder(_bt);
}
#if UNITY_ANDROID
[MenuItem("AutoBuild/EmptySymbol")]
static void EmptySymbol()
{
PlayerSettings.SetScriptingDefineSymbols(UnityEditor.Build.NamedBuildTarget.Android, "");
}
[MenuItem("AutoBuild/Set_AndroidKeyStore")]
static void Set_AndroidKeyStore()
{
PlayerSettings.Android.useCustomKeystore = true;
PlayerSettings.Android.keystoreName = "AndroidData/mysticatshop.keystore";
PlayerSettings.Android.keyaliasName = "nerdnavis";
PlayerSettings.Android.keystorePass = PlayerSettings.Android.keyaliasPass = "nerdnavis!!098";
}
[MenuItem("AutoBuild/Set_OneStoreKeyStore")]
static void Set_OneStoreKeyStore()
{
PlayerSettings.Android.useCustomKeystore = true;
PlayerSettings.Android.keystoreName = "AndroidData/mysticatshop_One.keystore";
PlayerSettings.Android.keyaliasName = "nerdnavis";
PlayerSettings.Android.keystorePass = PlayerSettings.Android.keyaliasPass = "nerdnavis!!098";
}
[MenuItem("AutoBuild/Build APK with KeyStore")]
static void Build_APK_with_KeyStore()
{
Common(BuildTargetGroup.Android, BuildTarget.Android);
Set_AndroidKeyStore();
//PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel23;
var curVer = PlayerSettings.bundleVersion;
PlayerSettings.bundleVersion = "9.9.9";
EditorUserBuildSettings.buildAppBundle = false;
BuildStart(path_dev + Application.version + "_(" + PlayerSettings.Android.bundleVersionCode + ").apk", BuildTarget.Android);
PlayerSettings.bundleVersion = curVer;
Common_End();
}
[MenuItem("AutoBuild/Build APK with KeyStore (OneStore)")]
static void Build_APK_OneStore()
{
Common(BuildTargetGroup.Android, BuildTarget.Android);
Set_OneStoreKeyStore();
//PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel23;
EditorUserBuildSettings.buildAppBundle = false;
BuildStart(path_dev_onestore + Application.version + "_(" + PlayerSettings.Android.bundleVersionCode + ").apk", BuildTarget.Android);
Common_End();
}
[MenuItem("AutoBuild/Build Live AAB")]
static void Build_AAB()
{
++PlayerSettings.Android.bundleVersionCode;
Common(BuildTargetGroup.Android, BuildTarget.Android);
Set_AndroidKeyStore();
PlayerSettings.SetScriptingDefineSymbols(UnityEditor.Build.NamedBuildTarget.Android, "");
EditorUserBuildSettings.buildAppBundle = true;
BuildStart(path_live + Application.version + "_(" + PlayerSettings.Android.bundleVersionCode + ").aab", BuildTarget.Android);
Common_End();
//if (EditorUtility.DisplayDialog("라이브 빌드", "현재 버전 : " + PlayerSettings.bundleVersion + " " + PlayerSettings.Android.bundleVersionCode.ToString(), "OK", "Not Build"))
// AppBundlePublisher.Build();
}
[MenuItem("AutoBuild/Build Live AAB (OneStore)")]
static void Build_AAB_OneStore()
{
++PlayerSettings.Android.bundleVersionCode;
Common(BuildTargetGroup.Android, BuildTarget.Android);
Set_OneStoreKeyStore();
PlayerSettings.SetScriptingDefineSymbols(UnityEditor.Build.NamedBuildTarget.Android, "");
EditorUserBuildSettings.buildAppBundle = true;
BuildStart(path_live_onestore + Application.version + "_(" + PlayerSettings.Android.bundleVersionCode + ").aab", BuildTarget.Android);
Common_End();
//if (EditorUtility.DisplayDialog("라이브 빌드", "현재 버전 : " + PlayerSettings.bundleVersion + " " + PlayerSettings.Android.bundleVersionCode.ToString(), "OK", "Not Build"))
// AppBundlePublisher.Build();
}
#endif
#if UNITY_IOS
[MenuItem("AutoBuild/IOS Xcode")]
static void Build_IOS_Xcode()
{
PlayerSettings.iOS.buildNumber = (int.Parse(PlayerSettings.iOS.buildNumber) + 1).ToString();
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS, "FGB_LIVE");
Common(BuildTargetGroup.iOS, BuildTarget.iOS);
BuildStart("IOSData/IOSXcodeLive_" + Application.version + "_" + PlayerSettings.iOS.buildNumber, BuildTarget.iOS);
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS, "");
}
[MenuItem("AutoBuild/IOS Xcode Test")]
static void Build_IOS_Xcode_Test()
{
PlayerSettings.iOS.buildNumber = (int.Parse(PlayerSettings.iOS.buildNumber) + 1).ToString();
Common(BuildTargetGroup.iOS, BuildTarget.iOS);
BuildStart("IOSData/IOSSRDebugOn_" + Application.version + "_" + PlayerSettings.iOS.buildNumber, BuildTarget.iOS);
}
[MenuItem("AutoBuild/Test IOS Simulator")]
static void Build_Test_IOS_Simulator()
{
Common(BuildTargetGroup.iOS, BuildTarget.iOS);
PlayerSettings.iOS.sdkVersion = iOSSdkVersion.SimulatorSDK;
BuildStart("IOSData/IOSTestBuild_" + Application.version + "_" + PlayerSettings.iOS.buildNumber, BuildTarget.iOS);
}
#endif
[PostProcessBuild(1)]
static void OnPostProcessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
#if UNITY_IOS
Debug.Log("OnPostProcessBuild : " + path);
var plistPath = Path.Combine(path, "Info.plist");
var plist = new PlistDocument();
plist.ReadFromFile(plistPath);
PlistElementDict allowsDict = plist.root.CreateDict("NSAppTransportSecurity");
allowsDict.SetBoolean("NSAllowsArbitraryLoads", true);
PlistElementDict exceptionsDict = allowsDict.CreateDict("NSExceptionDomains");
PlistElementDict domainDict = exceptionsDict.CreateDict("amazonaws.com");
domainDict.SetBoolean("NSExceptionAllowsInsecureHTTPLoads", true);
domainDict.SetBoolean("NSIncludesSubdomains", true);
domainDict = exceptionsDict.CreateDict("inonotebook.iptime.org");
domainDict.SetBoolean("NSExceptionAllowsInsecureHTTPLoads", true);
domainDict.SetBoolean("NSIncludesSubdomains", true);
plist.root.SetBoolean("GADIsAdManagerApp", true);
plist.root.SetBoolean("FirebaseMessagingAutoInitEnabled", false);
plist.root.SetBoolean("FirebaseAppStoreReceiptURLCheckEnabled", false);
plist.root.SetBoolean("ITSAppUsesNonExemptEncryption", false);
plist.root.SetString("NSAdvertisingAttributionReportEndpoint", "https://appsflyer-skadnetwork.com/");
// Add string setting
// SKAdNetwork IDs integration(for iOS14+)
var arraySKAdNetworkItems = plist.root.CreateArray("SKAdNetworkItems");
foreach (string id in SKAdNetworkIdentifiers)
{
var dict = arraySKAdNetworkItems.AddDict();
dict.SetString("SKAdNetworkIdentifier", id);
}
plist.root.SetString("FacebookAppID", FacebookSettings.AppId);
plist.root.SetString("FacebookDisplayName", Application.productName);
// Add URL Scheme
var array = plist.root.CreateArray("CFBundleURLTypes");
var urlDict = array.AddDict();
urlDict.SetString("CFBundleURLName", Application.identifier);
var urlInnerArray = urlDict.CreateArray("CFBundleURLSchemes");
urlInnerArray.AddString("fb" + FacebookSettings.AppId);
var googleiosurlscheme = urlDict.CreateArray("CFBundleURLSchemes");
googleiosurlscheme.AddString("com.googleusercontent.apps.107333004578-n74n1eaap2bgtg2od6ia1vk4ls5c139j");
var fbArray = plist.root.CreateArray("LSApplicationQueriesSchemes");
fbArray.AddString("fbapi");
fbArray.AddString("fb-messenger-api");
fbArray.AddString("fbauth2");
fbArray.AddString("fbshareextension");
plist.WriteToFile(plistPath);
// get project info
string pbxPath = PBXProject.GetPBXProjectPath(path);
var proj = new PBXProject();
proj.ReadFromFile(pbxPath);
var guid = proj.GetUnityMainTargetGuid();
var unitytest = proj.TargetGuidByName(PBXProject.GetUnityTestTargetName());
var unityframework = proj.GetUnityFrameworkTargetGuid();
proj.AddBuildProperty(guid, "OTHER_LDFLAGS", "-lc++");
proj.SetBuildProperty(guid, "ENABLE_BITCODE", "NO");
proj.SetBuildProperty(guid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
proj.SetBuildProperty(unitytest, "ENABLE_BITCODE", "NO");
proj.SetBuildProperty(unityframework, "ENABLE_BITCODE", "NO");
proj.SetBuildProperty(unityframework, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "NO");
// Add Push framework
proj.AddFrameworkToProject(guid, "UserNotifications.framework", false);
proj.WriteToFile(pbxPath);
// get entitlements path
string[] idArray = Application.identifier.Split('.');
var entitlementsPath = $"Unity-iPhone/{idArray[idArray.Length - 1]}.entitlements";
// create capabilities manager
var capManager = new ProjectCapabilityManager(pbxPath, entitlementsPath, null, guid);
// Add necessary capabilities
capManager.AddPushNotifications(true);
capManager.AddSignInWithApple();
capManager.AddBackgroundModes(BackgroundModesOptions.RemoteNotifications);
// Write to file
capManager.WriteToFile();
Debug.Log("OnPostProcessBuild end");
#endif
}
}
[PostProcessBuild(45)]//must be between 40 and 50 to ensure that it's not overriden by Podfile generation (40) and that it's added before "pod install" (50)
public static void FixPodFile(BuildTarget buildTarget, string buildPath)
{
if (buildTarget != BuildTarget.iOS)
{
return;
}
Debug.Log("FixPodFile");
using (StreamWriter sw = File.AppendText(buildPath + "/Podfile"))
{
sw.WriteLine("post_install do |installer|");
sw.WriteLine("installer.generated_projects.each do |project|");
sw.WriteLine("project.targets.each do |target|");
sw.WriteLine("target.build_configurations.each do |config|");
sw.WriteLine("config.build_settings[\"DEVELOPMENT_TEAM\"] = \"788PYWMPC6\"");
sw.WriteLine("end\nend\nend\nend");
}
//string podfilePath = Path.Combine(buildPath, "Podfile");
//if (File.Exists(podfilePath))
//{
// string[] lines = File.ReadAllLines(podfilePath);
// for (int i = 0; i < lines.Length; i++)
// {
// if (lines[i].Contains("Firebase/Auth"))
// lines[i] = " pod 'Firebase/Auth', '10.28.1'";
// else if (lines[i].Contains("GoogleSignIn"))
// lines[i] = " pod 'GoogleSignIn', '5.0.0'";
// else if (lines[i].Contains("GTMSessionFetcher/Core'"))
// lines[i] = " pod 'GTMSessionFetcher/Core'', '2.1'";
// }
// File.WriteAllLines(podfilePath, lines);
//}
}
private static readonly string[] SKAdNetworkIdentifiers = new string[]
{
"v9wttpbfk9.skadnetwork",
"n38lu8286q.skadnetwork",
"su67r6k2v3.skadnetwork",
"9t245vhmpl.skadnetwork",
"m8dbw4sv7c.skadnetwork",
"tl55sbb4fm.skadnetwork",
"wzmmz9fp6w.skadnetwork",
"4fzdc2evr5.skadnetwork",
"294l99pt4k.skadnetwork",
"4w7y6s5ca2.skadnetwork",
"7ug5zh24hu.skadnetwork",
"5l3tpt7t6e.skadnetwork",
"t38b2kh725.skadnetwork",
"c6k4g5qg8m.skadnetwork",
"wg4vff78zm.skadnetwork",
"v72qych5uu.skadnetwork",
"mp6xlyr22a.skadnetwork",
"cstr6suwn9.skadnetwork",
"4pfyvq9l8r.skadnetwork",
"w9q455wk68.skadnetwork",
"44jx6755aq.skadnetwork",
"k6y4y55b64.skadnetwork",
"8s468mfl3y.skadnetwork",
"mlmmfzh3r3.skadnetwork",
"4dzt52r2t5.skadnetwork",
"ydx93a7ass.skadnetwork",
"3qy4746246.skadnetwork",
"prcb7njmu6.skadnetwork",
"v79kvwwj4g.skadnetwork",
"glqzh8vgby.skadnetwork",
"s39g8k73mm.skadnetwork",
"238da6jt44.skadnetwork",
"424m5254lk.skadnetwork",
"5a6flpkh64.skadnetwork",
"hs6bdukanm.skadnetwork",
"x44k69ngh6.skadnetwork",
"a8cz6cu7e5.skadnetwork",
"488r3q3dtq.skadnetwork",
"32z4fx6l9h.skadnetwork",
"22mmun2rn5.skadnetwork",
"3rd42ekr43.skadnetwork",
"6yxyv74ff7.skadnetwork",
"ppxm28t8ap.skadnetwork",
"lr83yxwka7.skadnetwork",
"578prtvx9j.skadnetwork",
"a2p9lx4jpn.skadnetwork",
"av6w8kgt66.skadnetwork",
"4468km3ulz.skadnetwork",
"9rd848q2bz.skadnetwork",
"5lm9lj6jb7.skadnetwork",
"f7s53z58qe.skadnetwork",
"k674qkevps.skadnetwork",
"kbd757ywx3.skadnetwork",
"yclnxrl5pm.skadnetwork",
"zq492l623r.skadnetwork",
"zmvfpc5aq8.skadnetwork",
"f73kdq92p3.skadnetwork",
"f38h382jlk.skadnetwork",
"2u9pt9hc89.skadnetwork",
"5tjdwbrq8w.skadnetwork",
"3sh42y64q3.skadnetwork",
"9nlqeag3gk.skadnetwork"
};
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e7f08087432fca54d824025a36d4f1aa

View File

@ -0,0 +1,506 @@
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.AI;
public static class MyEditorUtil
{
struct TransformData
{
public Vector3 localPosition;
public Quaternion localRotation;
public Vector3 localScale;
public TransformData(Vector3 localPosition, Quaternion localRotation, Vector3 localScale)
{
this.localPosition = localPosition;
this.localRotation = localRotation;
this.localScale = localScale;
}
}
private static TransformData _data;
[MenuItem("Edit/Copy Transform Values &%#q", false, -101)]
public static void CopyTransformValues()
{
if (Selection.gameObjects.Length == 0) return;
var selectionTr = Selection.gameObjects[0].transform;
_data = new TransformData(selectionTr.localPosition, selectionTr.localRotation, selectionTr.localScale);
}
[MenuItem("Edit/Paste Transform Values &%#w", false, -101)]
public static void PasteTransformValues()
{
foreach (var selection in Selection.gameObjects)
{
Transform selectionTr = selection.transform;
Undo.RecordObject(selectionTr, "Paste Transform Values");
selectionTr.localPosition = _data.localPosition;
selectionTr.localRotation = _data.localRotation;
selectionTr.localScale = _data.localScale;
}
}
[MenuItem("Edit/Del All Child", false, -102)]
public static void DelAllChild()
{
foreach (var selection in Selection.gameObjects)
{
Transform selectionTr = selection.transform;
for (int i = 0; i < selectionTr.childCount; i++)
{
GameObject.DestroyImmediate(selectionTr.GetChild(i).gameObject);
--i;
}
}
}
[MenuItem("Edit/Set Mob Data", false, -103)]
public static void SetMobData()
{ // 설정된 몹 프리팹을 지우고 몹 데이터만 남긴다. 해당 데이터로 게임 내에서 몹을 동적 로딩한다.
//foreach (var selection in Selection.gameObjects)
//{
// var mobs = selection.GetComponentsInChildren<Actor>();
// var dic_wave = new Dictionary<string, LoadMobData>();
// for (int i = 0; i < mobs.Length; i++)
// {
// var parent = mobs[i].transform.parent;
// if (!dic_wave.ContainsKey(parent.name))
// {
// var loaddata = parent.GetComponent<LoadMobData>();
// if (loaddata == null) loaddata = parent.gameObject.AddComponent<LoadMobData>();
// dic_wave.Add(parent.name, loaddata);
// }
// var detaildata = new LoadMobDetailData
// {
// m_Position = mobs[i].transform.position,
// m_Rotation = mobs[i].transform.rotation,
// m_Role = mobs[i].m_Role,
// m_SubRole = mobs[i].m_SubRole,
// m_MagicID = (mobs[i] as MobActor).MagicID,
// };
// if (mobs[i].name.Contains("("))
// {
// var split = mobs[i].name.Split(' ');
// detaildata.m_PrefabName = "";
// if (split.Length == 2)
// detaildata.m_PrefabName = split[0];
// else
// for (int j = 0; j < split.Length - 1; j++)
// {
// if (j == split.Length - 2)
// detaildata.m_PrefabName += split[j];
// else
// detaildata.m_PrefabName += split[j] + " ";
// }
// }
// else
// detaildata.m_PrefabName = mobs[i].name;
// var witch = mobs[i] as WitchEnemyActor;
// if (witch != null)
// detaildata.list_MagicID = witch.list_MagicID;
// dic_wave[parent.name].list_mobdata.Add(detaildata);
// GameObject.DestroyImmediate(mobs[i].gameObject);
// }
//}
}
[MenuItem("Edit/GetFolderFileNames", false, -105)]
public static void GetFolderFileNames()
{
DirectoryInfo di = new DirectoryInfo("Assets/ResWork/UIPrefabs/Title");
var ext = ".prefab"; // ".mat";
var filenames = "";
foreach (FileInfo file in di.GetFiles("*" + ext))
filenames += file.Name.Replace(ext, "") + "\n";
Debug.Log(filenames);
}
private static List<ObjectSettings> objectSettingsList = new List<ObjectSettings>();
[MenuItem("Edit/MobSetting &s", false, -1001)]
static void MobSetting()
{
//var selectedObjects = Selection.gameObjects;
//foreach (var go in selectedObjects)
//{
// var ma = go.GetComponent<MobActor>();
// if (ma == null) ma = go.AddComponent<MobActor>();
// var m_bc = go.GetComponent<BoxCollider>();
// if (m_bc == null) m_bc = go.AddComponent<BoxCollider>();
// CommonSetting(go, ObstacleAvoidanceType.LowQualityObstacleAvoidance, 0, "Actor");
//}
}
[MenuItem("Edit/PetSetting", false, -1002)]
static void PetSetting()
{
//var selectedObjects = Selection.gameObjects;
//foreach (var go in selectedObjects)
//{
// var ma = go.GetComponent<PetActor>();
// if (ma == null) ma = go.AddComponent<PetActor>();
// ma.m_Role = eRole.Pet;
// CommonSetting(go, ObstacleAvoidanceType.NoObstacleAvoidance, 99, "Actor");
//}
}
[MenuItem("Edit/PCSetting", false, -1003)]
static void PCSetting()
{
//var selectedObjects = Selection.gameObjects;
//foreach (var go in selectedObjects)
//{
// // 프리팹 에셋 경로 가져오기
// string prefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(go);
// // Prefab인지 확인
// if (!string.IsNullOrEmpty(prefabPath))
// {
// // Prefab Asset 가져오기
// var prefabAsset = PrefabUtility.LoadPrefabContents(prefabPath);
// var ma = prefabAsset.GetComponent<WizardActor>();
// if (ma == null) ma = prefabAsset.AddComponent<WizardActor>();
// ma.m_Role = eRole.PC;
// var navmesh = prefabAsset.GetComponent<NavMeshAgent>();
// if (navmesh == null) navmesh = prefabAsset.AddComponent<NavMeshAgent>();
// navmesh.speed = 6f;
// navmesh.angularSpeed = 300f;
// navmesh.acceleration = 140f;
// navmesh.stoppingDistance = 4f;
// navmesh.autoBraking = true;
// navmesh.radius = 0.4f;
// navmesh.height = 2f;
// navmesh.avoidancePriority = 50;
// navmesh.obstacleAvoidanceType = ObstacleAvoidanceType.LowQualityObstacleAvoidance;
// Set_CommonRigidBody(prefabAsset, "Actor");
// var cc = prefabAsset.GetComponent<CapsuleCollider>();
// if (cc == null) cc = prefabAsset.AddComponent<CapsuleCollider>();
// cc.center = Vector3.up * 0.79f;
// cc.radius = 0.55f;
// cc.height = 1.58f;
// cc.direction = 1;
// var pcactor = prefabAsset.GetComponent<PCActor>();
// List<Transform> list_tr = new List<Transform>();
// // 프리팹 내부 수정
// var wristL = FindDeepChild(prefabAsset.transform, "Wrist_L");
// var wristR = FindDeepChild(prefabAsset.transform, "Wrist_R");
// if (wristL != null)
// {
// var strName = "w_shield";
// var shield = FindDeepChild(prefabAsset.transform, strName);
// if (shield == null) shield = new GameObject(strName).transform;
// shield.parent = wristL;
// shield.localPosition = new Vector3(0.0184f, -0.0158f, 0.0261f);
// shield.localEulerAngles = new Vector3(-21.84897f, 162.9937f, -171.4406f);
// shield.localScale = Vector3.one * 0.6f;
// list_tr.Add(shield);
// }
// if (wristR != null)
// {
// var strName = "w_righthand";
// var mace = FindDeepChild(prefabAsset.transform, strName);
// if (mace == null) mace = new GameObject(strName).transform;
// mace.parent = wristR;
// mace.localPosition = new Vector3(-0.0749f, 0.1431f, -0.024f);
// mace.localEulerAngles = new Vector3(0, 0, 10.6f);
// mace.localScale = new Vector3(1f, 0.7f, 1f);
// list_tr.Add(mace);
// }
// if (wristR != null)
// {
// var strName = "w_onehand";
// var onehand = FindDeepChild(prefabAsset.transform, strName);
// if (onehand == null) onehand = new GameObject(strName).transform;
// onehand.parent = wristR;
// onehand.localPosition = new Vector3(-0.0546f, -0.001f, -0.0271f);
// onehand.localEulerAngles = new Vector3(0f, 107.83f, 0f);
// onehand.localScale = Vector3.one * 0.8f;
// list_tr.Add(onehand);
// }
// if (wristL != null)
// {
// var strName = "w_bladedancer_l";
// var bd_l = FindDeepChild(prefabAsset.transform, strName);
// if (bd_l == null) bd_l = new GameObject(strName).transform;
// bd_l.parent = wristL;
// bd_l.localPosition = new Vector3(0.0413f, 0.015f, 0.0255f);
// bd_l.localEulerAngles = new Vector3(6.747354f, -168.0254f, 157.2563f);
// bd_l.localScale = Vector3.one;
// list_tr.Add(bd_l);
// }
// if (wristR != null)
// {
// var strName = "w_bladedancer_r";
// var bd_r = FindDeepChild(prefabAsset.transform, strName);
// if (bd_r == null) bd_r = new GameObject(strName).transform;
// bd_r.parent = wristR;
// bd_r.localPosition = new Vector3(-0.0546f, -0.001f, -0.0271f);
// bd_r.localEulerAngles = new Vector3(0f, 107.83f, 0f);
// bd_r.localScale = Vector3.one * 0.8f;
// list_tr.Add(bd_r);
// }
// if (wristR != null)
// {
// var strName = "w_orb";
// var bd_r = FindDeepChild(prefabAsset.transform, strName);
// if (bd_r == null) bd_r = new GameObject(strName).transform;
// bd_r.parent = wristR;
// bd_r.localPosition = new Vector3(-0.05439695f, -0.00136376f, -0.02748983f);
// bd_r.localEulerAngles = new Vector3(-1.791351f, 107.4387f, 1.349683f);
// bd_r.localScale = Vector3.one * 0.1f;
// list_tr.Add(bd_r);
// }
// if (wristL != null)
// {
// var strName = "w_archer_l";
// var bow_l = FindDeepChild(prefabAsset.transform, strName);
// if (bow_l == null) bow_l = new GameObject(strName).transform;
// bow_l.parent = wristL;
// bow_l.localPosition = new Vector3(0.069f, -0.01f, 0.034f);
// bow_l.localEulerAngles = new Vector3(-2.139496f, 0f, -3.480011f);
// bow_l.localScale = Vector3.one * 1.3f;
// list_tr.Add(bow_l);
// }
// pcactor.tfs_weapon = list_tr.ToArray();
// // 수정한 프리팹 저장
// PrefabUtility.SaveAsPrefabAsset(prefabAsset, prefabPath);
// PrefabUtility.UnloadPrefabContents(prefabAsset);
// }
// else
// {
// Debug.LogError($"{go.name}은(는) 프리팹 인스턴스가 아닙니다!");
// }
//}
}
static Transform FindDeepChild(Transform parent, string name)
{
foreach (Transform child in parent)
{
if (child.name == name)
return child;
var result = FindDeepChild(child, name);
if (result != null)
return result;
}
return null;
}
static void CommonSetting(GameObject go, ObstacleAvoidanceType avoid, int avoidancePriority, string layer)
{
Set_Collideer(go);
var navmesh = go.GetComponent<NavMeshAgent>();
if (navmesh == null) navmesh = go.AddComponent<NavMeshAgent>();
navmesh.speed = 4.5f;
navmesh.angularSpeed = 300f;
navmesh.acceleration = 140f;
navmesh.stoppingDistance = 0.1f;
navmesh.autoBraking = true;
navmesh.radius = 0.25f;
navmesh.avoidancePriority = avoidancePriority;
navmesh.obstacleAvoidanceType = avoid;
Set_CommonRigidBody(go, layer);
}
static void Set_CommonRigidBody(GameObject go, string layer)
{
go.layer = LayerMask.NameToLayer(layer);
var rigidbody = go.GetComponent<Rigidbody>();
if (rigidbody == null) rigidbody = go.AddComponent<Rigidbody>();
rigidbody.useGravity = false;
rigidbody.mass = 1;
rigidbody.linearDamping = 0;
rigidbody.angularDamping = 0.05f;
rigidbody.automaticCenterOfMass = true;
rigidbody.automaticInertiaTensor = true;
rigidbody.collisionDetectionMode = CollisionDetectionMode.Discrete;
rigidbody.constraints = RigidbodyConstraints.FreezeAll;
}
private static void UpdateAllObjects()
{
for (int i = objectSettingsList.Count - 1; i >= 0; i--)
{
var settings = objectSettingsList[i];
if (settings.tick < 0)
{
var bounds = settings.m_smr.bounds;
settings.m_bc.center = bounds.center;
settings.m_bc.size = bounds.size + Vector3.up;
settings.m_smr.updateWhenOffscreen = false;
settings.go.transform.localScale = settings.m_Scale;
objectSettingsList.RemoveAt(i);
}
else
{
settings.tick--;
}
}
if (objectSettingsList.Count == 0)
{
EditorApplication.update -= UpdateAllObjects;
}
}
private class ObjectSettings
{
public GameObject go;
public Vector3 m_Scale;
public BoxCollider m_bc;
public SkinnedMeshRenderer m_smr;
public int tick;
}
[MenuItem("Edit/SetCollider", false, -2000)]
static void SetCollider()
{
foreach (var go in Selection.gameObjects)
Set_Collideer(go);
}
static void Set_Collideer(GameObject go)
{
var objData = new ObjectSettings { go = go, m_Scale = go.transform.localScale, tick = 10 };
go.transform.localScale = Vector3.one;
var m_bc = go.GetComponent<BoxCollider>();
if (m_bc == null) m_bc = go.AddComponent<BoxCollider>();
objData.m_bc = m_bc;
var rds = go.GetComponentsInChildren<SkinnedMeshRenderer>();
SkinnedMeshRenderer m_smr = null;
if (rds.Length == 1) m_smr = rds[0];
else
{
for (int i = 0; i < rds.Length; i++)
{
if (rds[i].name.ToLower().Contains("skin"))
{
m_smr = rds[i];
break;
}
}
if (m_smr == null) m_smr = rds[0];
}
m_smr.updateWhenOffscreen = true;
objData.m_smr = m_smr;
objectSettingsList.Add(objData);
EditorApplication.update += UpdateAllObjects;
}
[MenuItem("Tools/Del_AddrResource")]
static void Del_AddrResource()
{
bool result = EditorUtility.DisplayDialog(
"경고!", // 제목
"모든 어드레서블 데이터를 삭제하시겠습니까?", // 메시지
"확인", // 확인 버튼
"취소" // 취소 버튼
);
if (result)
{
Caching.ClearCache();
EditorUtility.DisplayDialog("알림", "삭제 완료", "확인");
}
}
[MenuItem("Tools/MobActorImageFill", false, 1)]
static void MobActorImageFill()
{
var srs = Selection.activeGameObject.GetComponentsInChildren<SpriteRenderer>();
for (int i = 0; i < srs.Length; i++)
{
var child = srs[i];
if (child.name == "sr_actor")
child.GetComponent<SpriteRenderer>().sprite =
AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Res_Addr/Monster/10001.png");
}
}
[MenuItem("Tools/MobActorImageEmpty", false, 2)]
static void MobActorImageEmpty()
{
var srs = Selection.activeGameObject.GetComponentsInChildren<SpriteRenderer>();
for (int i = 0; i < srs.Length; i++)
{
var child = srs[i];
if (child.name == "sr_actor")
child.GetComponent<SpriteRenderer>().sprite = null;
}
}
//[MenuItem("Edit/Find All Particle", false, -103)]
//public static void FindAllParticle()
//{
// foreach (var selection in Selection.gameObjects)
// {
// var pats = selection.transform.GetComponentsInChildren<ParticleSystem>();
// for (int i = 0; i < pats.Length; i++)
// Debug.Log(pats[i].name);
// }
//}
//[MenuItem("Ino/Test %g")]
//static void Ino_Test()
//{
// ModelImporter modelImporter = (ModelImporter)AssetImporter.GetAtPath("Assets/ResWork/Quirky Series Vol.1 [v1.3]/Arctic Vol.1/Animations/Penguin_Animations.fbx");
// for (int i = 0; i < modelImporter.clipAnimations.Length; i++)
// {
// switch(modelImporter.clipAnimations[i].name)
// {
// case "Attack":
// break;
// }
// }
//}
//[MenuItem("Ino/Test")]
//static void FindHUD()
//{
// Debug.Log("시작");
// for (int i = 0; i < Selection.gameObjects.Length; i++)
// {
// var hud = Selection.gameObjects[i].transform.Find("HUD");
// if (hud == null) Debug.Log(Selection.gameObjects[i]);
// }
// Debug.Log("끝");
//}
//[MenuItem("Ino/Bip001 to Bip002")]
//static void ChangeBip01Bip02()
//{
// var tf = Selection.activeGameObject.transform;
// ChangeName(tf);
//}
// 자식까지 포함해서 레이어를 재귀적으로 설정하는 함수
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3334bb993485f194bb4fd632194b1518

497
Assets/Editor/MyUIUtil.cs Normal file
View File

@ -0,0 +1,497 @@
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
public static class MyUIUtil
{
static string path_whitebg = "Assets/ResWork/UI_Image/Common/whitebg.png";
static void SetLayerRecursively(GameObject obj, string newLayer = "UI")
{
obj.layer = LayerMask.NameToLayer(newLayer);
foreach (Transform child in obj.transform)
SetLayerRecursively(child.gameObject, newLayer); // 재귀 호출로 자식까지 처리
}
[MenuItem("GameObject/UI/New Image &i")] // &i는 Alt + I를 의미
static void CreateUIImage()
{
GameObject imageObject = new GameObject("New Image", typeof(Image));
// 생성된 오브젝트에 대한 Undo 기록 추가
Undo.RegisterCreatedObjectUndo(imageObject, "New Image");
var img = imageObject.GetComponent<Image>();
img.raycastTarget = false;
img.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path_whitebg);
if (Selection.activeGameObject != null)
{
imageObject.transform.SetParent(Selection.activeGameObject.transform, false);
SetLayerRecursively(imageObject);
// 새로 생성된 오브젝트를 선택
Selection.activeGameObject = imageObject;
// 씬 뷰에서 해당 오브젝트에 포커스 맞추기
//SceneView.lastActiveSceneView.FrameSelected();
}
}
[MenuItem("GameObject/UI/Text Only &t")] // &t는 Alt + T를 의미
static void CreateUITextMeshPro()
{
GameObject textObject = new GameObject("New TMP", typeof(TextMeshProUGUI));
// 생성된 오브젝트에 대한 Undo 기록 추가
Undo.RegisterCreatedObjectUndo(textObject, "New TMP");
var tm = textObject.GetComponent<TextMeshProUGUI>();
tm.raycastTarget = false;
tm.alignment = TextAlignmentOptions.Midline;
tm.textWrappingMode = TextWrappingModes.NoWrap;
if (Selection.activeGameObject != null)
{
textObject.transform.SetParent(Selection.activeGameObject.transform, false);
SetLayerRecursively(textObject);
// 새로 생성된 오브젝트를 선택
Selection.activeGameObject = textObject;
// 씬 뷰에서 해당 오브젝트에 포커스 맞추기
//SceneView.lastActiveSceneView.FrameSelected();
}
}
[MenuItem("GameObject/UI/Text - Local &y")]
static void CreateUITextMeshProWithLocal()
{
GameObject textObject = new GameObject("Local TMP", typeof(TextMeshProUGUI));
// 생성된 오브젝트에 대한 Undo 기록 추가
Undo.RegisterCreatedObjectUndo(textObject, "Local TMP");
textObject.AddComponent<SetLocalText>();
var tm = textObject.GetComponent<TextMeshProUGUI>();
tm.raycastTarget = false;
tm.alignment = TextAlignmentOptions.Midline;
tm.textWrappingMode = TextWrappingModes.NoWrap;
if (Selection.activeGameObject != null)
{
textObject.transform.SetParent(Selection.activeGameObject.transform, false);
SetLayerRecursively(textObject);
// 새로 생성된 오브젝트를 선택
Selection.activeGameObject = textObject;
// 씬 뷰에서 해당 오브젝트에 포커스 맞추기
//SceneView.lastActiveSceneView.FrameSelected();
}
}
[MenuItem("GameObject/UI/Button &b")] // Alt + B: Button 생성 (Image, Button, PlayClickSound_Only 추가)
static void CreateUIButton()
{
// 버튼 오브젝트 생성 (Image, Button 컴포넌트 포함)
GameObject buttonObject = new GameObject("btn_");
// 생성된 오브젝트에 대한 Undo 기록 추가
Undo.RegisterCreatedObjectUndo(buttonObject, "btn_");
buttonObject.AddComponent<Image>().sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path_whitebg);
buttonObject.AddComponent<Button>();
buttonObject.AddComponent<PlayClickSound_Only>();
// TextMeshPro-UGUI 생성 및 설정
GameObject textObject = new GameObject("btnName", typeof(TextMeshProUGUI));
textObject.AddComponent<SetLocalText>();
TextMeshProUGUI tmp = textObject.GetComponent<TextMeshProUGUI>();
tmp.text = "Button";
tmp.alignment = TextAlignmentOptions.Midline;
tmp.raycastTarget = false;
// Text 객체를 Button의 자식으로 설정
textObject.transform.SetParent(buttonObject.transform, false);
RectTransform textRect = textObject.GetComponent<RectTransform>();
textRect.anchorMin = Vector2.zero;
textRect.anchorMax = Vector2.one;
textRect.offsetMin = Vector2.zero;
textRect.offsetMax = Vector2.zero;
// 버튼 오브젝트의 부모 설정
if (Selection.activeGameObject != null)
{
buttonObject.transform.SetParent(Selection.activeGameObject.transform, false);
SetLayerRecursively(buttonObject);
// 새로 생성된 오브젝트를 선택
Selection.activeGameObject = buttonObject;
// 씬 뷰에서 해당 오브젝트에 포커스 맞추기
//SceneView.lastActiveSceneView.FrameSelected();
}
}
[MenuItem("GameObject/UI/My Slider &%#s")] // Alt + Ctrl + Shift + S: Slider 생성
static void CreateUISlider()
{
var slider = Make_Slider("Slider_", true, Color.yellow, 0.3f);
var sliderbase = slider.gameObject.AddComponent<SliderBase>();
sliderbase.m_slider = slider;
// 생성된 오브젝트에 대한 Undo 기록 추가
Undo.RegisterCreatedObjectUndo(slider.gameObject, "Slider_");
var middleslider = Make_Slider("SliderMiddle_", false, Color.red, 0.6f);
sliderbase.m_middleslider = middleslider;
middleslider.transform.SetParent(slider.transform, false);
middleslider.transform.SetSiblingIndex(1);
}
static Slider Make_Slider(string name, bool makebg, Color color, float slidervalue)
{
GameObject sliderObject = new GameObject(name, typeof(Slider));
sliderObject.GetComponent<RectTransform>().sizeDelta = new Vector2(200f, 20f);
Slider slider = sliderObject.GetComponent<Slider>();
slider.value = slidervalue;
if (makebg)
{
// Slider의 자식 오브젝트로 Background 이미지 추가
GameObject backgroundObject = new GameObject("Background", typeof(Image));
backgroundObject.transform.SetParent(sliderObject.transform, false);
var img = backgroundObject.GetComponent<Image>();
img.raycastTarget = false;
img.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path_whitebg);
RectTransform bgRect = backgroundObject.GetComponent<RectTransform>();
bgRect.anchorMin = new Vector2(0, 0);
bgRect.anchorMax = new Vector2(1, 1);
bgRect.offsetMin = new Vector2(0, 0); // Custom Stretch 적용
bgRect.offsetMax = new Vector2(0, 0); // Custom Stretch 적용
}
else
{
RectTransform sliderRect = slider.transform as RectTransform;
sliderRect.anchorMin = new Vector2(0, 0);
sliderRect.anchorMax = new Vector2(1, 1);
sliderRect.offsetMin = new Vector2(0, 0); // Custom Stretch 적용
sliderRect.offsetMax = new Vector2(0, 0); // Custom Stretch 적용
}
// Fill Area 오브젝트 추가
GameObject fillAreaObject = new GameObject("Fill Area", typeof(RectTransform));
fillAreaObject.transform.SetParent(sliderObject.transform, false);
RectTransform fillAreaRect = fillAreaObject.GetComponent<RectTransform>();
fillAreaRect.anchorMin = new Vector2(0, 0);
fillAreaRect.anchorMax = new Vector2(1, 1);
fillAreaRect.offsetMin = new Vector2(0, 0); // Custom Stretch 적용
fillAreaRect.offsetMax = new Vector2(0, 0); // Custom Stretch 적용
// Fill Area 자식으로 Fill 이미지 추가
GameObject fillObject = new GameObject("Fill", typeof(Image));
fillObject.transform.SetParent(fillAreaObject.transform, false);
slider.targetGraphic = fillObject.GetComponent<Image>();
slider.targetGraphic.color = color;
slider.targetGraphic.raycastTarget = false;
var fillimg = slider.targetGraphic as Image;
fillimg.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path_whitebg);
fillimg.type = Image.Type.Filled;
fillimg.fillMethod = Image.FillMethod.Horizontal;
RectTransform fillRect = fillObject.GetComponent<RectTransform>();
fillRect.anchorMin = new Vector2(0, 0);
fillRect.anchorMax = new Vector2(1, 1);
fillRect.offsetMin = new Vector2(0, 0);
fillRect.offsetMax = new Vector2(0, 0); // Stretch left 적용
// Fill 이미지 설정
slider.fillRect = fillRect;
if (Selection.activeGameObject != null)
{
sliderObject.transform.SetParent(Selection.activeGameObject.transform, false);
SetLayerRecursively(sliderObject);
// 새로 생성된 오브젝트를 선택
Selection.activeGameObject = sliderObject;
// 씬 뷰에서 해당 오브젝트에 포커스 맞추기
//SceneView.lastActiveSceneView.FrameSelected();
}
return slider;
}
[MenuItem("GameObject/UI/My CheckUI &%#c")]
static void CreateUICheck()
{
// 0. "check_" GameObject 생성
GameObject gocheck = new GameObject("check_");
// 생성된 오브젝트에 대한 Undo 기록 추가
Undo.RegisterCreatedObjectUndo(gocheck, "check_");
gocheck.AddComponent<RectTransform>();
// 1. 자식으로 크기 27x27 Image 추가
GameObject imageObject1 = new GameObject("checkbg", typeof(Image));
imageObject1.GetComponent<Image>().sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path_whitebg);
imageObject1.transform.SetParent(gocheck.transform, false); // gocheck의 자식으로 설정
RectTransform image1Rect = imageObject1.GetComponent<RectTransform>();
image1Rect.sizeDelta = new Vector2(50, 50); // 크기 설정
// 버튼 및 버튼음 추가
imageObject1.AddComponent<Button>();
imageObject1.AddComponent<PlayClickSound_Only>();
// 2. 1 의 자식으로 크기 39x30 Image 추가
GameObject imageObject2 = new GameObject("check", typeof(Image));
imageObject2.GetComponent<Image>().sprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/ResWork/UI/NewRes/bg/checkmark.png");
imageObject2.transform.SetParent(imageObject1.transform, false); // imageObject1의 자식으로 설정
RectTransform image2Rect = imageObject2.GetComponent<RectTransform>();
image2Rect.sizeDelta = new Vector2(50, 37); // 크기 설정
// 3. 1의 오른쪽에 1과 같은 깊이로 TMP 추가
GameObject textObject = new GameObject("checkName", typeof(TextMeshProUGUI));
textObject.AddComponent<SetLocalText>();
textObject.transform.SetParent(gocheck.transform, false); // gocheck의 자식으로 설정
RectTransform textRect = textObject.GetComponent<RectTransform>();
textRect.sizeDelta = new Vector2(100, 30); // TMP의 기본 크기 설정
textRect.anchoredPosition = new Vector2(image1Rect.sizeDelta.x + 50, 0); // Image1의 오른쪽으로 배치
TextMeshProUGUI tmp = textObject.GetComponent<TextMeshProUGUI>();
tmp.alignment = TextAlignmentOptions.MidlineLeft;
tmp.text = "체크 UI";
tmp.raycastTarget = false;
// 현재 선택된 오브젝트를 부모로 설정 (선택된 오브젝트가 있을 때만)
if (Selection.activeGameObject != null)
{
gocheck.transform.SetParent(Selection.activeGameObject.transform, false);
// 새로 생성된 오브젝트를 선택
Selection.activeGameObject = gocheck;
// 씬 뷰에서 해당 오브젝트에 포커스 맞추기
//SceneView.lastActiveSceneView.FrameSelected();
}
}
[MenuItem("GameObject/UI/My Scroll &#v")] // Alt + Shift + V 단축키
static void CreateUIScrollview()
{
// 0. "scrollview_" GameObject 생성
GameObject go_sv = new GameObject("scrollview_");
// 생성된 오브젝트에 대한 Undo 기록 추가
Undo.RegisterCreatedObjectUndo(go_sv, "scrollview_");
var img = go_sv.AddComponent<Image>();
img.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path_whitebg);
var sr = go_sv.AddComponent<ScrollRect>();
sr.horizontal = false;
// 1. 자식으로 Viewport 추가
GameObject go_vp = new GameObject("Viewport", typeof(Image));
go_vp.AddComponent<Mask>().showMaskGraphic = false;
go_vp.transform.SetParent(go_sv.transform, false); // 자식으로 설정
var rt_vp = go_vp.GetComponent<RectTransform>(); // 이미 추가된 RectTransform 가져오기
rt_vp.anchorMin = Vector2.zero;
rt_vp.anchorMax = Vector2.one;
rt_vp.pivot = Vector2.up;
rt_vp.offsetMin = Vector2.zero; // Left와 Bottom을 0으로 설정
rt_vp.offsetMax = Vector2.zero; // Right와 Top을 0으로 설정
var img_vp = go_vp.GetComponent<Image>();
img_vp.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path_whitebg);
// ScrollRect의 Viewport를 설정
sr.viewport = rt_vp;
// 2. 1의 자식으로 Content 추가
GameObject go_ct = new GameObject("Content");
go_ct.transform.SetParent(go_vp.transform, false); // Content를 Viewport의 자식으로 설정
go_ct.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize; // ContentSizeFitter 설정
var rt_ct = go_ct.GetComponent<RectTransform>();
rt_ct.anchorMin = Vector2.up;
rt_ct.anchorMax = Vector2.one;
rt_ct.pivot = Vector2.up;
rt_ct.offsetMin = Vector2.zero; // Left와 Bottom을 0으로 설정
rt_ct.offsetMax = Vector2.zero; // Right와 Top을 0으로 설정
// ScrollRect의 content 설정
sr.content = rt_ct;
// 현재 선택된 오브젝트를 부모로 설정 (선택된 오브젝트가 있을 때만)
if (Selection.activeGameObject != null)
{
go_sv.transform.SetParent(Selection.activeGameObject.transform, false);
SetLayerRecursively(go_sv);
// 새로 생성된 오브젝트를 선택
Selection.activeGameObject = go_sv;
// 씬 뷰에서 해당 오브젝트에 포커스 맞추기
//SceneView.lastActiveSceneView.FrameSelected();
}
}
[MenuItem("GameObject/UI/TMP InputField &%#i")] // Alt + Ctrl + Shift + i 단축키
static void CreateTMPInputField()
{
// 0. GameObject 생성
GameObject go_if = new GameObject("inputfield_");
// 생성된 오브젝트에 대한 Undo 기록 추가
Undo.RegisterCreatedObjectUndo(go_if, "inputfield_");
var img = go_if.AddComponent<Image>();
img.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path_whitebg);
img.color = new Color32(175, 175, 175, 255);
var tmp_if = go_if.AddComponent<TMP_InputField>();
var rt_if = go_if.GetComponent<RectTransform>();
rt_if.sizeDelta = new Vector2(160, 50);
// 1. 자식으로 Text Area 추가
GameObject go_vp = new GameObject("Text Area", typeof(RectMask2D), typeof(RectTransform));
go_vp.GetComponent<RectMask2D>().padding = new Vector4(-8, -5, -8, -5);
go_vp.transform.SetParent(go_if.transform, false); // 자식으로 설정
var rt_vp = go_vp.GetComponent<RectTransform>();
Set_RectTransform_CenterStretch(rt_vp);
tmp_if.textViewport = rt_vp;
// 2. 1의 자식으로 Placeholder 추가
GameObject go_ph = new GameObject("Placeholder", typeof(RectTransform));
go_ph.transform.SetParent(go_vp.transform, false); // 자식으로 설정
var rt_ph = go_ph.GetComponent<RectTransform>();
Set_RectTransform_CenterStretch(rt_ph);
var text_ph = go_ph.AddComponent<TextMeshProUGUI>();
text_ph.raycastTarget = false;
text_ph.textWrappingMode = TextWrappingModes.NoWrap;
text_ph.alignment = TextAlignmentOptions.Midline;
text_ph.text = "";
text_ph.color = Color.black;
text_ph.fontSize = 30;
tmp_if.placeholder = text_ph;
go_ph.AddComponent<LayoutElement>().ignoreLayout = true;
// 3. 1의 자식으로 Text 추가
GameObject go_txt = new GameObject("Text", typeof(RectTransform));
go_txt.transform.SetParent(go_vp.transform, false); // 자식으로 설정
var rt_txt = go_txt.GetComponent<RectTransform>();
Set_RectTransform_CenterStretch(rt_txt);
var text_txt = go_txt.AddComponent<TextMeshProUGUI>();
text_txt.raycastTarget = false;
text_txt.textWrappingMode = TextWrappingModes.NoWrap;
text_txt.alignment = TextAlignmentOptions.Midline;
text_txt.color = Color.black;
tmp_if.textComponent = text_txt;
tmp_if.pointSize = 30;
// 현재 선택된 오브젝트를 부모로 설정 (선택된 오브젝트가 있을 때만)
if (Selection.activeGameObject != null)
{
go_if.transform.SetParent(Selection.activeGameObject.transform, false);
SetLayerRecursively(go_if);
// 새로 생성된 오브젝트를 선택
Selection.activeGameObject = go_if;
// 씬 뷰에서 해당 오브젝트에 포커스 맞추기
//SceneView.lastActiveSceneView.FrameSelected();
}
}
static void Set_RectTransform_CenterStretch(RectTransform rtf)
{
rtf.anchorMin = Vector2.zero;
rtf.anchorMax = Vector2.one;
rtf.pivot = new Vector2(0.5f, 0.5f);
rtf.offsetMin = Vector2.zero;
rtf.offsetMax = Vector2.zero;
}
[MenuItem("GameObject/UI/My Tab H %#q")] // Ctrl + Shift + Q: 탭 생성 (가로)
static void CreateUITab_Hori()
{
Set_Tab_Common<HorizontalLayoutGroup>("tab_layout_h");
}
[MenuItem("GameObject/UI/My Tab V %#w")] // Ctrl + Shift + W: 탭 생성 (세로)
static void CreateUITab_Veti()
{
Set_Tab_Common<VerticalLayoutGroup>("tab_layout_v");
}
static void Set_Tab_Common<T>(string layoutName) where T : HorizontalOrVerticalLayoutGroup
{
GameObject obj = new GameObject(layoutName);
var layout = obj.AddComponent<T>();
layout.childForceExpandWidth = false;
layout.childForceExpandHeight = false;
layout.spacing = 5;
var tabbase = obj.AddComponent<TabUIBase>();
tabbase.m_Content = obj;
obj.GetComponent<RectTransform>().sizeDelta = Vector2.zero;
Undo.RegisterCreatedObjectUndo(obj, layoutName);
GameObject tab = new GameObject("tab_dontDelete");
tabbase.go_card = tab;
tab.transform.SetParent(obj.transform, false);
var rt = tab.AddComponent<RectTransform>();
rt.sizeDelta = new Vector2(300, 100);
var tabcard = tab.AddComponent<TabCardBase>();
tabcard.images = new Image[2];
tabcard.texts = new TextMeshProUGUI[1];
GameObject tab_bg = new GameObject("i_bg");
tab_bg.transform.SetParent(tab.transform, false);
var i_bg = tab_bg.AddComponent<Image>();
tabcard.images[0] = i_bg;
var bgRect = tab_bg.GetComponent<RectTransform>();
bgRect.anchorMin = new Vector2(0, 0);
bgRect.anchorMax = new Vector2(1, 1);
bgRect.offsetMin = new Vector2(0, 0); // Custom Stretch 적용
bgRect.offsetMax = new Vector2(0, 0); // Custom Stretch 적용
i_bg.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path_whitebg);
tabcard.m_btn = tab_bg.AddComponent<Button>();
tab_bg.AddComponent<PlayClickSound_Only>();
GameObject tab_img = new GameObject("i_img");
tab_img.transform.SetParent(tab.transform, false);
var i_img = tab_img.AddComponent<Image>();
tabcard.images[1] = i_img;
i_img.rectTransform.sizeDelta = new Vector2(100, 50);
i_img.raycastTarget = false;
i_img.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path_whitebg);
i_img.color = Color.black;
tab_img.AddComponent<Button>();
tab_img.AddComponent<PlayClickSound_Only>();
// TextMeshPro-UGUI 생성 및 설정
GameObject textObject = new GameObject("btnName", typeof(TextMeshProUGUI));
//textObject.AddComponent<LocalizeStringEvent>();
TextMeshProUGUI tmp = textObject.GetComponent<TextMeshProUGUI>();
tabcard.texts[0] = tmp;
tmp.raycastTarget = false;
tmp.text = "Tab";
tmp.alignment = TextAlignmentOptions.Midline;
// Text 객체의 부모 설정
textObject.transform.SetParent(tab.transform, false);
RectTransform textRect = textObject.GetComponent<RectTransform>();
textRect.anchorMin = Vector2.zero;
textRect.anchorMax = Vector2.one;
textRect.offsetMin = Vector2.zero;
textRect.offsetMax = Vector2.zero;
// 버튼 오브젝트의 부모 설정
if (Selection.activeGameObject != null)
{
obj.transform.SetParent(Selection.activeGameObject.transform, false);
SetLayerRecursively(obj);
// 새로 생성된 오브젝트를 선택
Selection.activeGameObject = obj;
// 씬 뷰에서 해당 오브젝트에 포커스 맞추기
//SceneView.lastActiveSceneView.FrameSelected();
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 843a27393872fef44b875537c93822ab

View File

@ -0,0 +1,20 @@
#if UNITY_EDITOR
using UnityEditor;
public class SpriteSingleModeImporter : AssetPostprocessor
{
const int PostProcessOrder = 0;
public override int GetPostprocessOrder() => PostProcessOrder;
void OnPreprocessTexture()
{
var importer = assetImporter as TextureImporter;
if (importer.importSettingsMissing is false)
return;
importer.spriteImportMode = SpriteImportMode.Single;
importer.mipmapEnabled = false;
}
}
#endif

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 277602c7288246242b11a5008f725350

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8d94ea3741ad8d344a76cca0aa62f67e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Plugins.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 37892793e608e8248b098847128be0c3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 80ab48d20daeb5b4cb0bc1e64f5acd64
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 52c313066dda63c459f7c826dbb8eed1
folderAsset: yes
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,2 @@
Please, check out latest Anti-Cheat Toolkit Code Library API Docs here:
https://codestage.net/uas_files/actk/api/

View File

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: f4c375fd5bde53c42b0ebc6ee503f7a1
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/API.txt
uploadId: 773557

View File

@ -0,0 +1,519 @@
# Changelog
Changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
#### Types of changes
- **Added** for new features.
- **Changed** for changes in existing functionality.
- **Deprecated** for soon-to-be removed features.
- **Removed** for now removed features.
- **Fixed** for any bug fixes.
- **Security** in case of vulnerabilities.
💡 _Always remove previous plugin version before updating_
## [6.2.0] - 2025-07-18
### Added
- Add TimeScale watching to the SpeedHackDetector
- New `WatchTimeScale` property (enabled by default) to detect unauthorized Time.timeScale modifications
- New `SpeedHackDetector.SetTimeScale()` API for safe timeScale changes
- New `SpeedHackDetector.AllowAnyTimeScaleFor()` API for temporary third-party asset control
- New `SpeedHackDetector.AllowAnyTimeScale()` / `StopAllowingAnyTimeScale()` APIs for indefinite control
- New `SpeedHackProofTime.timeScale` property as drop-in replacement for Time.timeScale
## [6.1.0] - 2025-06-23
### Added
- Add ObscuredDateTimeOffset type
- Add ObscuredGuid type
## [6.0.0] - 2025-04-09
### Changed
- Change versioning to be year-agnostic (following Unity decision)
### Fixed
- Fix false positives after changing serialized obscured variables in ScriptableObject at PlayMode
- Fix GC allocations from ObscuredFloat in newer Unity versions
## [2024.3.5] - 2025-03-04
### Fixed
- Fix obscured types migration and fixing when used in nested types (thx sol3breaker)
## [2024.3.4] - 2025-02-28
### Fixed
- Fix a false positives from inconsistent hashing (thx Dmitry Statsenko & Icaro)
## [2024.3.3] - 2024-12-26
### Fixed
- Fix a false positive while editing ObscuredString in Play Mode (thx Thiago)
## [2024.3.2] - 2024-12-19
### Fixed
- Fix a regression with error from empty ObscuredStrings editing in Inspector
## [2024.3.1] - 2024-12-18
### Changed
- Improve error handling in serialized data parser
### Fixed
- Fix possible exceptions while parsing serialized data
- Fix invalid variables Inspector highlight inside nested items
- Fix possible edge case obscured false positives
## [2024.3.0] - 2024-12-10
### Added
- Add obscured types validation / migration in Build scenes
### Changed
- Improve URP / HDRP compatibility
- Update icons
- Update changelog format and [release as html](https://docs.codestage.net/actk/changelog/)
### Security
- Fix SpeedHackDetector vulnerability
### Fixed
- Fix deprecation warnings when migrating from PlayerPrefs to ObscuredPrefs
## [2024.2.1] - 2024-11-18
### Fixed
- Fix default ObscuredBool was rendered as True in Inspector (thx sol3breaker)
## [2024.2.0] - 2024-11-17
### Added
- Add Project View context menus to validate or migrate specified assets
### Changed
- Improve RAM usage while validating or migrating assets
- Improve asset validation and migration API for more flexibility
### Fixed
- Fix possible exceptions while iterating scripting objects
- Fix non-initialized obscured variables were marked as invalid
## [2024.1.0] - 2024-11-04
### Added
- Add Honeypot option to the ObscuredCheatingDetector
### Changed
- Improve Obscured types cheating resistance
- Improve ObscuredTypesNewtonsoftConverter performance
- Improve ObscuredVector2Int API compatibility
- Improve ObscuredVector3Int API compatibility
### Security
- Fix few reported vulnerabilities
### Deprecated
- Deprecate ACTK_OBSCURED_AUTO_MIGRATION flag with auto-migration from legacy versions
### Fixed
- Fix AndroidScreenRecordingBlocker example
- Fix rare ObscuredBigInteger data corruption
- Fix ObscuredUInt inspector couldn't be set to values more than 2147483647
## [2024.0.0] - 2024-07-07
### Added
- Add prevent screen recording feature for Android platform
### Changed
- Improve Unity 6 compatibility
- Increase minimum Android supported version to Android 5.0 (API SDK 21)
### Fixed
- Fix few compilation warnings
## [2023.2.6] - 2024-01-14
### Fixed
- Fix Obscured Types json serialization could produce exception in obfuscated build (thx Thiago)
## [2023.2.5] - 2024-01-04
### Fixed
- Fix ObscuredPrefs.HasKey() could return wrong value when migrating from v1 format (thx Avocco)
## [2023.2.4] - 2023-12-19
### Changed
- Make sure domain reload support is editor-only
### Fixed
- Fix CodeHashGenerator warnings in Editor
## [2023.2.3] - 2023-09-12
### Fixed
- Fix harmless errors in console while using Prefs Editor (thx Rono)
- Fix rare RuntimeInitializeOnLoadMethodAttribute errors (thx Silent)
## [2023.2.2] - 2023-07-08
### Changed
- Improve disabled domain reload compatibility (thx KonstantGames)
## [2023.2.1] - 2023-06-16
### Fixed
- Fix ObscuredFilePrefs didn't allow saving after removing a key (thx Tyle)
## [2023.2.0] - 2023-05-31
### Changed
- Improve ObscuredDateTime compatibility
- ObscuredDateTime.GetDecrypted() now returns DateTime instead of binary long value
- Improve AppInstallationSource accuracy for PackageInstaller source
- Make ObscuredBigInteger serialize into JSON as human-readable string instead of b64 bytes
### Fixed
- Fix wrong Culture could be used while deserializing obscured types from JSON (thx spikyworm5)
## [2023.1.0] - 2023-05-20
### Added
- Add ObscuredDateTime (thx spikyworm5)
### Changed
- Include ObscuredDecimal into the obscured types validation
### Fixed
- Fix ObscuredDecimal might not parse properly from the Inspector
- Fix ObscuredString equality check against regular string (thx haeggongs)
## [2023.0.1] - 2023-05-11
### Added
- Add switch for the ACTK_NEWTONSOFT_JSON conditional in ACTk Settings
### Fixed
- Fix CodeHashGeneratorPostprocessor.HashesGenerated event didn't invoke on post build step (thx mhosoya)
## [2023.0.0] - 2023-05-09
### Added
- Add AppInstallationSourceValidator to easily figure out Android app installation source
- Add ObscuredCheatingDetector.LastDetectionInfo property with detection context
- Add built-in Newtonsoft Json Converter for Obscured Types
- Add CodeHashGenerator.GenerateAsync() API
- Add CodeHashGeneratorPostprocessor APIs:
- CalculateBuildReportHashesAsync() method
- CalculateExternalBuildHashesAsync() method
- Add HashGeneratorResult.PrintToConsole() API for debugging purposes
- Add state corruption checks when API accessed too early (before Awake)
- Add Windows build hashing progress bar in Editor
- Add Normalize() method and normalized property to ObscuredVector2, ObscuredVector3, ObscuredQuaternion
- Add buildPath argument to CalculateExternalBuildHashes so you could calculate hashes for any build path from CLI
- Add migration notes to the User Manual to help you migrate from v2021 to v2023
### Changed
- Update minimum Unity version to 2019.4
- Improve Obscured Types equality checks
- Improve how ObscuredFile handles custom path in some rare cases
- Significantly improve CodeHashGenerator performance:
- Utilize all available cores in Editor's CodeHashGeneratorPostprocessor
- Utilize specified threads count in Runtime CodeHashGenerator
- Make Summary Hash generation magnitudes faster
- Change CodeHashGeneratorPostprocessor API:
- Refactor Instance.callbackOrder to static CallbackOrder
- Refactor Instance.HashesGenerated to static HashesGenerated
- Refactor HashesGenerated delegate `BuildHashes[]` hashedBuilds argument to `IReadOnlyList<BuildHashes> hashedBuilds`
- Refactor BuildHashes.FileHashes property type from Array to IReadOnlyList
- Refactor HashGeneratorResult.FileHashes property type from Array to IReadOnlyList
- Improve CodeHashGeneratorPostprocessor progress reporting in Editor
- Improve CodeHashGenerator filtering to include all .dex and .so files on Android
- Prepare CodeHashGenerator filtering to include content files so whole build could be covered in future
- Introduce various minor improvements
### Removed
- Remove static CodeHashGeneratorPostprocessor.Instance property
### Fixed
- Fix InjectionDetector build processor could keep the service temp file if build fails
- Fix ObscuredBigInteger.Equals(ObscuredBigInteger) check didn't work properly
- Fix ObscuredBigInteger.GetHashCode() did return value affected by random crypto key
- Fix ObscuredFile could have inconsistent path delimiters in the FilePath
- Fix some critical errors didn't print to console
- Fix regression where ACTK_PREVENT_READ_PHONE_STATE didn't remove permissions caused by SystemInfo.deviceUniqueIdentifier
## [2021.6.4] - 2023-03-09
_I know it's 2023 already, fine? xD_
### Changed
- Improve Unity 2023 compatibility
### Fixed
- Fix inspector fields regression introduced at v2021.2.1 for Unity versions below 2022.2 (thx mrm83)
- Fix possible SpeedHackDetector false positives regression introduced at v2021.3.0, now DSP module is optional and off by default with proper warning about its sensitivity (thx mrm83, Kazeon, gpedrani and others 🙏)
- Fix some buttons didn't open Project Settings in Unity 2019+
## [2021.6.3] - 2022-12-19
### Added
- Add few more operators to the ObscuredBigInteger to better match BigInteger API.
### Changed
- Change CodeHashGenerator Editor warning to error to make it more visible and reduce possible confusion
## [2021.6.2] - 2022-11-12
### Changed
- Make ObscuredCheatingDetector to print logs when ACTK_DETECTION_BACKLOGS is enabled
- Improve Obscured Types serialization Validation logs to include exact path and location
## [2021.6.1] - 2022-11-10
### Fixed
- Fix rare SpeedHackDetector false positive
## [2021.6.0] - 2022-11-09
### Added
- Add new WallHackDetector compatibility check and safety warning (thx naezith)
- Add serialization corruption detection for Obscured Types
### Changed
- Improve ObscuredVector2Int and ObscuredVector3Int vector components access performance
### Fixed
- Reduce rare SpeedHackDetector false positive possibility
- Fix few rare ObscuredCheatingDetector false positives (thx thiagolr)
## [2021.5.1] - 2022-09-10
### Changed
- Improve ObscuredFilePrefsAutoSaver behavior in Editor (thx YeahBoi)
### Fixed
- Fix ObscuredBigInteger corruption (thx jaeyoung)
- Fix ambiguous APIs at the ObscuredBigInteger
## [2021.5.0] - 2022-07-31
### Added
- Add IDisposable implementation to the SHA1Wrapper class
- Add DurationSeconds property to the CodeHashGenerator results
### Changed
- Improve CodeHashGenerator accuracy in Editor for IL2CPP platforms
- Deprecate few obsolete CodeHashGenerator APIs
- Improve Unity 2023 compatibility
### Fixed
- Fix WebGL compilation regression
## [2021.4.2] - 2022-07-25
### Fixed
- ObscuredCheatingDetector: fix possible rare false positive (thx tbiz5270)
## [2021.4.1] - 2022-07-21
### Fixed
- SpeedHackDetector: fix possible rare false positive in Editor
## [2021.4.0] - 2022-07-16
### Added
- Add LastOnlineTimeResult instance property to the TimeCheatingDetector
- Add automatic ProGuard configuration to prevent errors due to minification
- Add new menu item to configure proguard-user.txt on demand
### Changed
- Make ProGuard configuration more granular to obfuscate more of the native code
- Expose internal TimeCheatingDetector.IsReadyForForceCheck() API
### Fixed
- Fix possible TimeCheatingDetector error due to certificate validation (thx murat303)
## [2021.3.0] - 2022-07-10
### Changed
- Improve Speed Hack Detector sensitivity in sandboxed environments
- Improve detectors' keepAlive logic when using additive scenes
- Improve WebGL file system compatibility at Obscured File and Obscured File Prefs
### Fixed
- Fix possible undesired detector self-destroy on additive scene load
## [2021.2.1] - 2022-07-04
### Changed
- Change some property drawers to use Delayed fields to reduce CPU overhead while editing obscured fields in inspector
## [2021.2.0] - 2022-06-29
### Added
- Add ObscuredBigInteger type
- Add BigInteger type support to the ObscuredPrefs / ObscuredFilePrefs
- Add TriggerDetection() utility method to all detectors
- Add 'Trigger detection' context menu item to all detectors components
## [2021.1.1] - 2022-05-04
### Added
- Add TimeCheatingDetector.GetOnlineTimeTask() overloads with CancellationToken argument
## [2021.1.0] - 2022-04-11
### Added
- Add ObscuredFilePrefs Auto Save on mobile platforms (enabled by default)
- Automatically saves unsaved changes on app loose focus / pause
- Add API to disable ObscuredFilePrefs Auto Save (disables Auto Save on both mobile and non-mobile platforms)
- Introduce IObscuredFileSettings to improve API usage experience
### Changed
- Add locks to the ObscuredFilePrefs sync operations to improve stability when accessing it from different threads
- Move ObscuredFilePrefs Save-On-Quit code to the Auto Save feature entity so it's disableable now
### Fixed
- Prevent ObscuredFilePrefs Save-On-Quit while not initialized
- Fix ObscuredFilePrefs behavior with disabled Reload Domain
- Fix compilation error at Unity 2018 Android
- Fix compilation warnings for WebGL platform
## [2021.0.10] - 2022-03-09
### Fixed
- Fix ObscuredString name in Inspector might render incorrect in arrays (thx Sungmin An)
## [2021.0.9] - 2022-03-06
### Changed
- CodeHashGenerator's Summary Hash is no longer printed for AAB builds
- Skip Android Patch Packages hashing by CodeHashGenerator
### Fixed
- Fix obsolete API usage leading to compilation error in Unity 2022.1
## [2021.0.8] - 2022-02-08
### Changed
- Minor Prefs Editor UI improvements
### Fixed
- Fix Prefs Editor window didn't update properly under specific conditions (thx Todd Gillissie)
## [2021.0.7] - 2021-11-18
### Fixed
- Fix iOS Conditional compilation constants settings could not apply in some Unity versions (thx Hesham)
- Fix empty ObscuredString fields automatic migration (thx thiagolr)
## [2021.0.6] - 2021-11-18
### Changed
- Warn when trying to use ObscuredFile with StreamingAssets on Android and WebGL (thx Harama)
### Fixed
- Fix automatic ObscuredString migration didn't happen properly in some cases (thx thiagolr)
- Fix exception in ObscuredFilePrefs on iOS could happen under rare conditions
- Fix ObscuredString example log
## [2021.0.5] - 2021-10-26
### Changed
- Improve ObscuredPrefs and ObscuredFilePrefs compatibility with Obscured types
### Fixed
- Fix TimeUtils could be disposed unexpectedly (thx Hesham)
- Fix TimeUtils might not reinitialize properly in rare case
## [2021.0.4] - 2021-10-02
### Fixed
- Fix BehaviorDesigner integration package compilation errors (thx Levent)
## [2021.0.3] - 2021-09-27
### Changed
- Improve TimeCheatingDetector performance a bit
### Fixed
- Fix missing script at the example scene
- Fix CodeHashGeneratorListener example compilation errors
## [2021.0.2] - 2021-09-17
### Fixed
- Fix empty string was read as null from ObscuredPrefs and ObscuredFilePrefs (thx C0dingschmuser)
## [2021.0.1] - 2021-09-10
### Changed
- Improve exceptions logging a bit
### Fixed
- Fix compilation exception for iOS platform (thx Vladnee)
## [2021.0.0] - 2021-09-06
### Added
- Add new ObscuredFile and ObscuredFilePrefs tools to the ACTk 🧰
- Encrypted and plain modes
- All modes have data consistency validation
- All modes have lock to device feature
- ObscuredFilePrefs has simple and easy to use PlayerPrefs-like APIs
- Async compatible
- Supports UWP starting from Unity 2019.1
- BehaviorDesigner and PlayMaker Actions
- Add generic APIs to ObscuredPrefs
- Add new types support to ObscuredPrefs:
- rest of simple C# types (SByte, Byte, Int16, UInt16, Char)
- System.DateTime
- Color (it's possible to save HDR colors now)
- Matrix4x4, RangeInt, Ray, Ray2D, RectInt, Vector2Int, Vector3Int, Vector4
- Add ObscuredQuaternion property drawer (now it's editable from inspector)
- Add automatic link.xml generation option to complement fix for WallHack Detector false positives due to stripping
- Add additional information to the important error logs
- Make ThreadSafeRandom utility public
- Add Copy Player Prefs path context menu item to the Prefs Editor tab
- Add ObscuredPrefs Vector2Int and Vector3Int support to BehaviorDesigner integration
- Add new support contact, let's chat at [Discord](https://discord.gg/Ppsb89naWf)!
### Changed
- Swap Changelog to md version to better match Unity packages format ([Keep a Changelog](https://keepachangelog.com/en/1.0.0/))
- Rename following ObscuredPrefs API in order to better suite coding style:
- OnAlterationDetected -> NotGenuineDataDetected
- OnPossibleForeignSavesDetected -> DataFromAnotherDeviceDetected
- lockToDevice -> DeviceLockLevel
- Move ObscuredPrefs.DeviceLockLevel enum out from the ObscuredPrefs type
- Introduce DeviceLockTamperingSensitivity instead of readForeignSaves and emergencyMode settings for additional clarity
- Decimal values processing at ObscuredPrefs are much faster now with much lesser GC-allocations footprint
- Improve exceptions handling across whole codebase
- Improve incorrect type usage handling at ObscuredPrefs (thx David E)
- Improve Settings UI a bit
- Improve detectors startup a bit
- Improve Prefs Editor error handling
- Minor code refactoring and cleanup
- Update some API docs
### Deprecated
- Deprecate non-generic ObscuredPrefs APIs (to be removed in future versions)
### Removed
- Remove .NET 3.5 scripting runtime version support
### Fixed
- Fix possible data corruption at all Obscured types in super rare scenarios (only one rare case for ObscuredBool was found)
- Fix possible false positives from WallHackDetector on Unity 2019.3 or newer when IL2CPP "Strip Engine Code" setting is used (thx Hesham)
- Fix compilation warning on UWP platform
- Fix redundant injection detector support were added into IL2CPP builds in some conditions
- Fix exceptions in Unity 2021.2 and newer while browsing ACTk settings
- Fix code hash pre-generation was run redundantly when building with Create Visual Studio Solution option enabled
- Fix Behavior Tree at BehaviorDesigner's integration ObscuredPrefsExample scene
- Fix other minor stuff here and there
## [2.3.4] and older
See older versions changelog in legacy text format [here](https://codestage.net/uas_files/actk/changelog-legacy.txt)

View File

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: a707e4914fc9f014790c02a3cd0b455d
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/CHANGELOG.md
uploadId: 773557

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ac5220baa6a51bb47a5c640ddf6c0ce8
folderAsset: yes
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,23 @@
{
"name": "ACTk.Editor",
"references": [
"ACTk.Runtime"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.modules.audio",
"expression": "",
"define": "UNITY_AUDIO_MODULE"
}
],
"noEngineReferences": false
}

View File

@ -0,0 +1,16 @@
fileFormatVersion: 2
guid: 8ef82b58ed1acc8419a02b8a05286620
timeCreated: 1554196096
licenseType: Store
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/ACTk.Editor.asmdef
uploadId: 773557

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1ff6bc3cba85fba4e922af0d4a624023
folderAsset: yes
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,47 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode
{
using System.IO;
using System.Linq;
using UnityEngine;
internal static class ACTkEditorConstants
{
internal static class Conditionals
{
public const string WallhackLinkXML = "ACTK_WALLHACK_LINK_XML";
public const string ExcludeObfuscation = "ACTK_EXCLUDE_OBFUSCATION";
public const string PreventReadPhoneState = "ACTK_PREVENT_READ_PHONE_STATE";
public const string PreventInternetPermission = "ACTK_PREVENT_INTERNET_PERMISSION";
public const string ThirdPartyIntegration = "ACTK_IS_HERE";
public const string UsExportCompatible = "ACTK_US_EXPORT_COMPATIBLE";
public const string NewtonsoftJson = "ACTK_NEWTONSOFT_JSON";
public const string InjectionDebug = "ACTK_INJECTION_DEBUG";
public const string InjectionDebugVerbose = "ACTK_INJECTION_DEBUG_VERBOSE";
public const string InjectionDebugParanoid = "ACTK_INJECTION_DEBUG_PARANOID";
public const string WallhackDebug = "ACTK_WALLHACK_DEBUG";
public const string DetectionBacklogs = "ACTK_DETECTION_BACKLOGS";
public const string GenericDevLogs = "ACTK_DEV_LOGS";
}
public const string SettingsProviderPath = "Code Stage/Anti-Cheat Toolkit";
public const string MenuPath = "Code Stage/🕵 Anti-Cheat Toolkit/";
public const string AssetsMenuPath = "Assets/Code Stage/🕵 Anti-Cheat Toolkit/";
public const string ToolsMenuPath = "Tools/" + MenuPath;
public const string GameObjectMenuPath = "GameObject/Create Other/" + MenuPath;
public static readonly string ProjectFolder = Path.GetFullPath(Path.Combine(Application.dataPath, "../"));
public static readonly string ProjectTempFolder = Path.Combine(ProjectFolder, "Temp");
public static readonly string ProjectLibraryFolder = Path.Combine(ProjectFolder, "Library");
public static readonly string ProjectSettingsFolder = Path.Combine(ProjectFolder, "ProjectSettings");
public static readonly string AssetsFolder = Path.Combine(ProjectFolder, "Assets");
public static readonly string[] HexTable = Enumerable.Range(0, 256).Select(v => v.ToString("x2")).ToArray();
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a9ee7d4a58674ac69c27bc7c29417309
timeCreated: 1552925230
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ACTkEditorConstants.cs
uploadId: 773557

View File

@ -0,0 +1,41 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode
{
using UnityEditor;
using UnityEngine;
/// <summary>
/// Use it to guess current directory of the Anti-Cheat Toolkit.
/// </summary>
public class ACTkMarker : ScriptableObject
{
/// <summary>
/// Returns raw path of the ACTkMarker script for further reference.
/// </summary>
/// <returns>Path of the ACTkMarker ScriptableObject asset.</returns>
public static string GetAssetPath()
{
string result;
var tempInstance = CreateInstance<ACTkMarker>();
var script = MonoScript.FromScriptableObject(tempInstance);
if (script != null)
{
result = AssetDatabase.GetAssetPath(script);
}
else
{
result = AssetDatabase.FindAssets("ACTkMarker")[0];
result = AssetDatabase.GUIDToAssetPath(result);
}
DestroyImmediate(tempInstance);
return result;
}
}
}

View File

@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 00b850c709170d1469bcd89e6f80b16c
timeCreated: 1557660868
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ACTkMarker.cs
uploadId: 773557

View File

@ -0,0 +1,233 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System;
using CodeStage.AntiCheat.Common;
using CodeStage.AntiCheat.Detectors;
using CodeStage.AntiCheat.EditorCode.PostProcessors;
using CodeStage.AntiCheat.EditorCode.Processors;
using UnityEditor;
using UnityEngine;
namespace CodeStage.AntiCheat.EditorCode
{
internal static class ACTkMenuItems
{
#if UNITY_6000_0_OR_NEWER
public const string BuildProfilesLabel = "Build Profiles";
#else
public const string BuildProfilesLabel = "Build Settings";
#endif
// ---------------------------------------------------------------
// Main menu items
// ---------------------------------------------------------------
[MenuItem(ACTkEditorConstants.ToolsMenuPath + "Settings...", false, 100)]
private static void ShowSettingsWindow()
{
ACTkSettings.Show();
}
[MenuItem(ACTkEditorConstants.ToolsMenuPath + "Injection Detector Whitelist Editor...", false, 1000)]
private static void ShowAssembliesWhitelistWindow()
{
UserWhitelistEditor.ShowWindow();
}
[MenuItem(ACTkEditorConstants.ToolsMenuPath + "Calculate external build hashes", false, 1200)]
private static async void HashExternalBuild()
{
try
{
var buildHashes = await CodeHashGeneratorPostprocessor.CalculateExternalBuildHashesAsync(null, true);
if (buildHashes == null || buildHashes.FileHashes.Count == 0)
{
Debug.LogError(ACTk.LogPrefix + "External build hashing was not successful. " +
"See previous log messages for possible details.");
}
}
catch (Exception e)
{
ACTk.PrintExceptionForSupport("External build hashing exception!", e);
}
}
[MenuItem(ACTkEditorConstants.ToolsMenuPath + "Configure proguard-user.txt", false, 1201)]
private static void CheckProGuard()
{
BuildPreProcessor.CheckProGuard(true);
}
[MenuItem(ACTkEditorConstants.ToolsMenuPath + "Migrate/Migrate obscured types in Prefabs and Scriptable Objects...", false, 1500)]
private static void MigrateObscuredTypesInAssets()
{
MigrateAssets();
}
[MenuItem(ACTkEditorConstants.ToolsMenuPath + "Migrate/Migrate obscured types in opened scenes...", false, 1501)]
private static void MigrateObscuredTypesInScene()
{
ObscuredTypesMigrator.MigrateOpenedScenes();
}
[MenuItem(ACTkEditorConstants.ToolsMenuPath + "Migrate/Migrate obscured types in " + BuildProfilesLabel + " scenes...",
false, 1502)]
private static void MigrateObscuredTypesInBuildSettingsScenes()
{
ObscuredTypesMigrator.MigrateBuildProfilesScenes();
}
[MenuItem(ACTkEditorConstants.ToolsMenuPath + "Validate/Validate obscured types in Prefabs and Scriptable Objects...", false, 1500)]
private static void ValidateObscuredTypesInAssets()
{
ValidateAssets();
}
private static void ValidateAssets(string[] assetPaths = null)
{
var invalidAssetsPaths = ObscuredTypesValidator.ValidateProjectAssets(assetPaths);
if (invalidAssetsPaths != null && invalidAssetsPaths.Length > 0)
{
var result = ConfirmMigrationAfterValidation();
switch (result)
{
case 0:
MigrateAssets(invalidAssetsPaths, true);
break;
case 2:
MigrateAssets(invalidAssetsPaths, true, true);
break;
}
}
}
private static void MigrateAssets(string[] assetPaths = null, bool fixOnly = false, bool skipInteraction = false)
{
ObscuredTypesMigrator.MigrateProjectAssets(assetPaths, fixOnly, skipInteraction);
}
[MenuItem(ACTkEditorConstants.ToolsMenuPath + "Validate/Validate obscured types in opened scenes...", false,
1501)]
private static void ValidateObscuredTypesInOpenedScenes()
{
ValidateObscuredTypesInScenes(() => ObscuredTypesValidator.ValidateOpenedScenes(),
(fixOnly, skipInteraction) => ObscuredTypesMigrator.MigrateOpenedScenes(fixOnly, skipInteraction));
}
[MenuItem(ACTkEditorConstants.ToolsMenuPath + "Validate/Validate obscured types in " + BuildProfilesLabel + " scenes...",
false, 1502)]
private static void ValidateObscuredTypesInBuildSettingsScenes()
{
ValidateObscuredTypesInScenes(() => ObscuredTypesValidator.ValidateBuildProfilesScenes(),
(fixOnly, skipInteraction) =>
ObscuredTypesMigrator.MigrateBuildProfilesScenes(fixOnly, skipInteraction));
}
private static void ValidateObscuredTypesInScenes(Func<int> validateFunc, Action<bool, bool> migrateFunc)
{
var invalidPropertiesFound = validateFunc();
if (invalidPropertiesFound > 0)
{
var result = ConfirmMigrationAfterValidation();
switch (result)
{
case 0:
migrateFunc(true, false);
break;
case 2:
migrateFunc(true, true);
break;
}
}
}
// ---------------------------------------------------------------
// GameObject menu items
// ---------------------------------------------------------------
[MenuItem(ACTkEditorConstants.GameObjectMenuPath + "All detectors", false, 0)]
private static void AddAllDetectorsToScene()
{
AddInjectionDetectorToScene();
AddObscuredCheatingDetectorToScene();
AddSpeedHackDetectorToScene();
AddWallHackDetectorToScene();
AddTimeCheatingDetectorToScene();
}
[MenuItem(ACTkEditorConstants.GameObjectMenuPath + InjectionDetector.ComponentName, false, 1)]
private static void AddInjectionDetectorToScene()
{
DetectorTools.SetupDetectorInScene<InjectionDetector>();
}
[MenuItem(ACTkEditorConstants.GameObjectMenuPath + ObscuredCheatingDetector.ComponentName, false, 1)]
private static void AddObscuredCheatingDetectorToScene()
{
DetectorTools.SetupDetectorInScene<ObscuredCheatingDetector>();
}
[MenuItem(ACTkEditorConstants.GameObjectMenuPath + SpeedHackDetector.ComponentName, false, 1)]
private static void AddSpeedHackDetectorToScene()
{
DetectorTools.SetupDetectorInScene<SpeedHackDetector>();
}
[MenuItem(ACTkEditorConstants.GameObjectMenuPath + WallHackDetector.ComponentName, false, 1)]
private static void AddWallHackDetectorToScene()
{
DetectorTools.SetupDetectorInScene<WallHackDetector>();
}
[MenuItem(ACTkEditorConstants.GameObjectMenuPath + TimeCheatingDetector.ComponentName, false, 1)]
private static void AddTimeCheatingDetectorToScene()
{
DetectorTools.SetupDetectorInScene<TimeCheatingDetector>();
}
// ---------------------------------------------------------------
// Project menu items
// ---------------------------------------------------------------
[MenuItem(ACTkEditorConstants.AssetsMenuPath + "Validate Obscured Types on Prefabs and Scriptable Objects", false, 200)]
private static void ValidateObscuredTypesInSelectedFolder()
{
var targetFiles = AssetTools.GetMigratableAssetsFilePaths(Selection.assetGUIDs);
ValidateAssets(targetFiles);
}
[MenuItem(ACTkEditorConstants.AssetsMenuPath + "Validate Obscured Types on Prefabs and Scriptable Objects", true, 200)]
private static bool ValidateObscuredTypesInSelectedFolderValidate()
{
var targetFiles = AssetTools.GetMigratableAssetsFilePaths(Selection.assetGUIDs);
return targetFiles != null && targetFiles.Length > 0;
}
[MenuItem(ACTkEditorConstants.AssetsMenuPath + "Migrate Obscured Types on Prefabs and Scriptable Objects", false, 200)]
private static void MigrateObscuredTypesInSelectedFolder()
{
var targetFiles = AssetTools.GetMigratableAssetsFilePaths(Selection.assetGUIDs);
MigrateAssets(targetFiles);
}
[MenuItem(ACTkEditorConstants.AssetsMenuPath + "Migrate Obscured Types on Prefabs and Scriptable Objects", true, 200)]
private static bool MigrateObscuredTypesInSelectedFolderValidate()
{
var targetFiles = AssetTools.GetMigratableAssetsFilePaths(Selection.assetGUIDs);
return targetFiles != null && targetFiles.Length > 0;
}
private static int ConfirmMigrationAfterValidation()
{
return EditorUtility.DisplayDialogComplex(ObscuredTypesValidator.ModuleName,
"Invalid types found. It's recommended to try migrate and / or fix them.\n" +
"Select 'Migrate and fix' only if you did update from the older ACTk version.",
"Migrate and fix", "Cancel", "Fix only");
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: e10289b3bdb7db64c8f2c624b337d331
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ACTkMenuItems.cs
uploadId: 773557

View File

@ -0,0 +1,10 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("CodeStage.ACTk.Service")]
[assembly: InternalsVisibleTo("CodeStage.ACTk.Tests.Editor")]

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a1cc293465f841498ad940f7813ccedd
timeCreated: 1588809689
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/AssemblyInfo.cs
uploadId: 773557

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d9e6164ee563b444d87cbe3437d6012a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b51e1480187f1ad4ab95559fe4ef5720
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,198 @@
#region copyright
// -------------------------------------------------------
// Copyright (C) Dmitry Yuhanov [https://codestage.net]
// -------------------------------------------------------
#endregion
namespace CodeStage.EditorCommon.Tools
{
using System;
using UnityEditor;
using UnityEngine;
internal static class CSColorTools
{
internal enum ColorKind
{
Green,
Red,
Purple
}
public const string GreenHex = "02C85F";
public const string GreenDarkHex = "02981F";
public const string PurpleHex = "A76ED1";
public const string PurpleDarkHex = "7030A0";
public const string RedHex = "FF4040";
public const string RedAltHex = "FF6060";
public const string RedDarkHex = "FF1010";
public const string BrightGreyHex = "E5E5E5";
public readonly static Color32 Green = new Color32(2, 200, 95, 255);
public readonly static Color32 GreenDark = new Color32(2, 152, 31, 255);
public readonly static Color32 Purple = new Color32(167, 110, 209, 255);
public readonly static Color32 PurpleDark = new Color32(112, 48, 160, 255);
public readonly static Color32 RedAlt = new Color32(255, 96, 96, 255);
public readonly static Color32 RedDark = new Color32(255, 16, 16, 255);
public readonly static Color32 BrightGrey = new Color32(229, 229, 229, 255);
public static string EditorGreenHex
{
get
{
return EditorGUIUtility.isProSkin ? GreenHex : GreenDarkHex;
}
}
public static string EditorPurpleHex
{
get
{
return EditorGUIUtility.isProSkin ? PurpleHex : PurpleDarkHex;
}
}
public static string EditorRedHex
{
get
{
return EditorGUIUtility.isProSkin ? RedAltHex : RedDarkHex;
}
}
public static Color EditorGreen
{
get
{
return EditorGUIUtility.isProSkin ? Green : GreenDark;
}
}
public static Color EditorPurple
{
get
{
return EditorGUIUtility.isProSkin ? Purple : PurpleDark;
}
}
public static Color EditorRed
{
get
{
return EditorGUIUtility.isProSkin ? RedAlt : RedDark;
}
}
public static Color DimmedColor
{
get
{
return ChangeAlpha(GUI.skin.label.normal.textColor, 150);
}
}
public static Color BrightGreyDimmed
{
get
{
return ChangeAlpha(BrightGrey, 150);
}
}
public static Color GreenColor
{
get
{
return LerpToGreen(GUI.skin.label.normal.textColor, 0.3f);
}
}
public static Color RedColor
{
get
{
return LerpToRed(GUI.skin.label.normal.textColor, 0.3f);
}
}
public static Color BackgroundGreenTint
{
get
{
return EditorGUIUtility.isProSkin ? new Color32(0, 255, 0, 150) : new Color32(0, 255, 0, 30);
}
}
public static Color BackgroundRedTint
{
get
{
return EditorGUIUtility.isProSkin ? new Color32(255, 0, 0, 150) : new Color32(255, 0, 0, 30);
}
}
public static string WrapBool(bool value)
{
return WrapString(value.ToString(), value ? ColorKind.Green : ColorKind.Red);
}
public static string WrapString(string inputGood, string inputBad, bool good)
{
return WrapString(good ? inputGood : inputBad, good ? ColorKind.Green : ColorKind.Red);
}
public static string WrapString(string input, bool good)
{
return WrapString(input, good ? ColorKind.Green : ColorKind.Red);
}
public static string WrapString(string input, ColorKind colorKind)
{
switch (colorKind)
{
case ColorKind.Green:
return WrapString(input, EditorGreenHex);
case ColorKind.Red:
return WrapString(input, EditorRedHex);
case ColorKind.Purple:
return WrapString(input, EditorPurpleHex);
default:
throw new ArgumentOutOfRangeException("colorKind", colorKind, null);
}
}
public static string WrapString(string input, Color color)
{
var colorString = ColorUtility.ToHtmlStringRGBA(color);
return WrapString(input, colorString);
}
// color argument should be in rrggbbaa format or match standard html color name, without '#'
public static string WrapString(string input, string color)
{
return "<color=#" + color + ">" + input + "</color>";
}
public static Color32 LerpToRed(Color32 inValue, float greenAmountPercent)
{
return Color.Lerp(inValue, Color.red, greenAmountPercent);
}
public static Color32 LerpToGreen(Color32 inValue, float greenAmountPercent)
{
return Color.Lerp(inValue, Color.green, greenAmountPercent);
}
public static Color32 LerpToYellow(Color32 inValue, float greenAmountPercent)
{
return Color.Lerp(inValue, Color.yellow, greenAmountPercent);
}
private static Color32 ChangeAlpha(Color32 inValue, byte alphaValue)
{
inValue.a = alphaValue;
return inValue;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9243bbab60b6a3b4bafdac4bdac4d91f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Common/Tools/CSColorTools.cs
uploadId: 773557

View File

@ -0,0 +1,36 @@
#region copyright
// -------------------------------------------------------
// Copyright (C) Dmitry Yuhanov [https://codestage.net]
// -------------------------------------------------------
#endregion
namespace CodeStage.EditorCommon.Tools
{
using UnityEngine;
internal static class CSEditorIcons
{
public static Texture AssetStore { get { return CSTextureLoader.GetIconTexture("Asset Store", ImageKind.InternalIcon); } }
public static Texture Error { get { return CSTextureLoader.GetIconTexture("console.erroricon", ImageKind.InternalIcon); } }
public static Texture ErrorSmall { get { return CSTextureLoader.GetIconTexture("console.erroricon.sml", ImageKind.InternalIcon); } }
public static Texture Favorite { get { return CSTextureLoader.GetIconTexture("Favorite", ImageKind.InternalIcon); } }
public static Texture FavoriteIcon { get { return CSTextureLoader.GetIconTexture("Favorite Icon", ImageKind.InternalIcon); } }
public static Texture FilterByType { get { return CSTextureLoader.GetIconTexture("FilterByType", ImageKind.InternalIcon); } }
public static Texture Folder { get { return CSTextureLoader.GetIconTexture("Folder Icon", ImageKind.InternalIcon); } }
public static Texture GameObject { get { return CSTextureLoader.GetTypeImage(typeof(GameObject)); } }
public static Texture Help { get { return CSTextureLoader.GetIconTexture("_Help", ImageKind.InternalIcon); } }
public static Texture HierarchyView { get { return CSTextureLoader.GetIconTexture("UnityEditor.SceneHierarchyWindow", ImageKind.InternalIcon); } }
public static Texture Info { get { return CSTextureLoader.GetIconTexture("console.infoicon", ImageKind.InternalIcon); } }
public static Texture InfoSmall { get { return CSTextureLoader.GetIconTexture("console.infoicon.sml", ImageKind.InternalIcon); } }
public static Texture Inspector { get { return CSTextureLoader.GetIconTexture("UnityEditor.InspectorWindow", ImageKind.InternalIcon); } }
public static Texture Prefab { get { return UnityEditorInternal.InternalEditorUtility.FindIconForFile("dummy.prefab"); } }
public static Texture ProjectView { get { return CSTextureLoader.GetIconTexture("Project", ImageKind.InternalIcon); } }
public static Texture Scene { get { return UnityEditorInternal.InternalEditorUtility.FindIconForFile("dummy.unity"); } }
public static Texture Script { get { return UnityEditorInternal.InternalEditorUtility.FindIconForFile("dummy.cs"); } }
public static Texture Search { get { return CSTextureLoader.GetIconTexture("Search Icon", ImageKind.InternalIcon); } }
public static Texture Settings { get { return CSTextureLoader.GetIconTexture("Settings", ImageKind.InternalIcon); } }
public static Texture Warn { get { return CSTextureLoader.GetIconTexture("console.warnicon", ImageKind.InternalIcon); } }
public static Texture WarnSmall { get { return CSTextureLoader.GetIconTexture("console.warnicon.sml", ImageKind.InternalIcon); } }
public static Texture Menu { get { return CSTextureLoader.GetIconTexture("_Menu@2x", ImageKind.InternalIcon); } }
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 66cb067166e1b12439fba7d0d810a0bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Common/Tools/CSEditorIcons.cs
uploadId: 773557

View File

@ -0,0 +1,27 @@
#region copyright
// -------------------------------------------------------
// Copyright (C) Dmitry Yuhanov [https://codestage.net]
// -------------------------------------------------------
#endregion
namespace CodeStage.EditorCommon.Tools
{
using System.IO;
internal static class CSFileTools
{
public static void DeleteFile(string path)
{
if (!File.Exists(path)) return;
RemoveReadOnlyAttribute(path);
File.Delete(path);
}
private static void RemoveReadOnlyAttribute(string filePath)
{
var attributes = File.GetAttributes(filePath);
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
File.SetAttributes(filePath, attributes & ~FileAttributes.ReadOnly);
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 64edcf809efa9c64c899a0bd87134443
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Common/Tools/CSFileTools.cs
uploadId: 773557

View File

@ -0,0 +1,83 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine.SceneManagement;
#if UNITY_6000_0_OR_NEWER
using UnityEditor.Build.Profile;
using System.Linq;
#endif
namespace CodeStage.EditorCommon.Tools
{
internal static class CSSceneTools
{
public static Scene[] GetOpenedScenes()
{
var openScenes = new Scene[SceneManager.sceneCount];
for (var i = 0; i < SceneManager.sceneCount; i++)
{
openScenes[i] = SceneManager.GetSceneAt(i);
}
return openScenes;
}
public static string[] GetOpenedValidScenesPaths()
{
var openedScenes = GetOpenedScenes();
var scenePaths = new List<string>();
foreach (var scene in openedScenes)
{
if (scene.IsValid())
scenePaths.Add(scene.path);
}
return scenePaths.ToArray();
}
public static string[] GetBuildProfilesScenesPaths(bool includeDisabled = false)
{
#if UNITY_6000_0_OR_NEWER
var scenePaths = new HashSet<string>(GetBuildSettingsSceneList(includeDisabled)); // uses EditorBuildSettings.scenes
var allBuildProfiles = AssetDatabase.FindAssets("t:BuildProfile");
foreach (var buildProfileGuid in allBuildProfiles)
{
var buildProfile = AssetDatabase.LoadAssetAtPath<BuildProfile>(AssetDatabase.GUIDToAssetPath(buildProfileGuid));
#if UNITY_6000_1_OR_NEWER
var overrideGlobalSceneList = buildProfile.overrideGlobalScenes;
#else
var overrideGlobalSceneList = false;
try
{
var overrideGlobalSceneListProperty = typeof(BuildProfile).GetProperty("overrideGlobalSceneList",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
overrideGlobalSceneList = overrideGlobalSceneListProperty != null &&
(bool)overrideGlobalSceneListProperty.GetValue(buildProfile);
}
catch { // ignored
}
#endif
if (!overrideGlobalSceneList)
continue;
foreach (var overridenSceneListScene in buildProfile.scenes)
{
scenePaths.Add(overridenSceneListScene.path);
}
}
return scenePaths.ToArray();
#else
return GetBuildSettingsSceneList().ToArray();
#endif
}
private static List<string> GetBuildSettingsSceneList(bool includeDisabled = false)
{
var scenes = EditorBuildSettings.scenes;
var scenePaths = new List<string>();
foreach (var scene in scenes)
{
if (includeDisabled || scene.enabled)
scenePaths.Add(scene.path);
}
return scenePaths;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0fe63d325ce40684ab7b869a405e8bbb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Common/Tools/CSSceneTools.cs
uploadId: 773557

View File

@ -0,0 +1,114 @@
#region copyright
// -------------------------------------------------------
// Copyright (C) Dmitry Yuhanov [https://codestage.net]
// -------------------------------------------------------
#endregion
namespace CodeStage.EditorCommon.Tools
{
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
internal enum ImageKind
{
External,
InternalTexture,
InternalIcon
}
internal static class CSTextureLoader
{
public static string ExternalTexturesFolder { get; set; }
public static string LogPrefix { get; set; }
private static readonly Dictionary<string, Texture> CachedTextures = new Dictionary<string, Texture>();
public static Texture GetTexture(string fileName)
{
return GetTexture(fileName, false);
}
public static Texture GetIconTexture(string fileName, ImageKind kind = ImageKind.External)
{
return GetTexture(fileName, true, kind);
}
private static Texture GetTexture(string fileName, bool icon, ImageKind kind = ImageKind.External)
{
Texture result;
var isDark = EditorGUIUtility.isProSkin;
var textureName = fileName;
if (isDark)
textureName = "d_" + textureName;
if (CachedTextures.ContainsKey(textureName))
{
result = CachedTextures[textureName];
}
else
{
var path = fileName;
if (kind == ImageKind.External)
{
fileName = textureName;
path = Path.Combine(ExternalTexturesFolder, "Textures");
if (icon)
path = Path.Combine(path, "Icons");
path = Path.Combine(path, fileName);
if (!File.Exists(Path.GetFullPath(path)) && !Path.HasExtension(path))
{
path = Path.ChangeExtension(path, "png");
}
if (!File.Exists(Path.GetFullPath(path)))
{
Debug.LogWarning("Couldn't find icon " + fileName + " at path " + path);
return null;
}
}
switch (kind)
{
case ImageKind.External:
result = AssetDatabase.LoadAssetAtPath(path, typeof(Texture2D)) as Texture2D;
break;
case ImageKind.InternalTexture:
result = EditorGUIUtility.FindTexture(path);
break;
case ImageKind.InternalIcon:
result = EditorGUIUtility.IconContent(path).image;
break;
default:
throw new ArgumentOutOfRangeException("kind", kind, null);
}
if (result == null)
Debug.LogError(LogPrefix + "Some error occurred while looking for image\n" + path);
else
CachedTextures[textureName] = result;
}
return result;
}
public static Texture GetTypeImage(Type type)
{
var key = type.ToString();
if (CachedTextures.ContainsKey(key))
{
return CachedTextures[key];
}
var texture = EditorGUIUtility.ObjectContent(null, type).image;
CachedTextures.Add(key, texture);
return texture;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 67cfa036dfc9bcf4292fa6a9f8b35555
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Common/Tools/CSTextureLoader.cs
uploadId: 773557

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f0c63cf6cabe3a547a0b46de95bff055
folderAsset: yes
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,64 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode.Editors
{
using Detectors;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof (InjectionDetector))]
internal class InjectionDetectorEditor : KeepAliveBehaviourEditor<InjectionDetector>
{
protected override bool DrawUniqueDetectorProperties()
{
if (!ACTkSettings.Instance.InjectionDetectorEnabled)
{
using (GUITools.Vertical(GUITools.PanelWithBackground))
{
EditorGUILayout.Separator();
EditorGUILayout.HelpBox("Injection Detector support is not enabled! Injection Detector will not work properly",
MessageType.Error, true);
using (new GUILayout.HorizontalScope())
{
if (GUILayout.Button("Enable Now"))
{
ACTkSettings.Instance.InjectionDetectorEnabled = true;
}
if (GUILayout.Button("Enable In settings..."))
{
ACTkSettings.Show();
}
}
EditorGUILayout.Separator();
}
return true;
}
if (SettingsUtils.IsIL2CPPEnabled())
{
EditorGUILayout.HelpBox("Mono Injections are not possible in IL2CPP, this detector is not needed in IL2CPP builds",
MessageType.Info, true);
return true;
}
if (!InjectionRoutines.IsTargetPlatformCompatible())
{
EditorGUILayout.HelpBox("Injection Detection is only supported in Standalone and Android builds",
MessageType.Warning, true);
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: 6025f911d895a7e43a5d5c6229254129
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Editors/InjectionDetectorEditor.cs
uploadId: 773557

View File

@ -0,0 +1,94 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode.Editors
{
using Common;
using UnityEditor;
using UnityEngine;
internal class KeepAliveBehaviourEditor<T> : Editor where T: KeepAliveBehaviour<T>
{
protected T self;
private SerializedProperty autoStart;
private SerializedProperty autoDispose;
private SerializedProperty keepAlive;
private SerializedProperty detectionEvent;
private SerializedProperty detectionEventHasListener;
public virtual void OnEnable()
{
autoStart = serializedObject.FindProperty("autoStart");
autoDispose = serializedObject.FindProperty("autoDispose");
keepAlive = serializedObject.FindProperty("keepAlive");
detectionEvent = serializedObject.FindProperty("detectionEvent");
detectionEventHasListener = serializedObject.FindProperty("detectionEventHasListener");
self = (T)target;
FindUniqueDetectorProperties();
}
public override void OnInspectorGUI()
{
if (self == null) return;
serializedObject.Update();
EditorGUIUtility.labelWidth = 140;
EditorGUILayout.Space();
DrawHeader("Base settings");
EditorGUILayout.PropertyField(autoStart);
detectionEventHasListener.boolValue = EditorTools.CheckUnityEventHasActivePersistentListener(detectionEvent);
CheckAdditionalEventsForListeners();
if (autoStart.boolValue && !detectionEventHasListener.boolValue && !AdditionalEventsHasListeners())
{
EditorGUILayout.LabelField(new GUIContent("You need to add at least one active item to the Events in order to use Auto Start feature!"), GUITools.BoldLabel);
}
else if (!autoStart.boolValue)
{
EditorGUILayout.LabelField(new GUIContent("Don't forget to start detection!", "You should start detector from code using ObscuredCheatingDetector.StartDetection() method. See readme for details."), GUITools.BoldLabel);
EditorGUILayout.Separator();
}
EditorGUILayout.PropertyField(autoDispose);
EditorGUILayout.PropertyField(keepAlive);
EditorGUILayout.Separator();
if (DrawUniqueDetectorProperties())
{
EditorGUILayout.Separator();
}
//DrawHeader("Events");
EditorGUILayout.PropertyField(detectionEvent);
DrawAdditionalEvents();
serializedObject.ApplyModifiedProperties();
EditorGUIUtility.labelWidth = 0;
}
protected virtual void DrawHeader(string text)
{
GUITools.DrawHeader(text);
}
protected virtual bool AdditionalEventsHasListeners()
{
return true;
}
protected virtual void FindUniqueDetectorProperties() {}
protected virtual bool DrawUniqueDetectorProperties() { return false; }
protected virtual void CheckAdditionalEventsForListeners() {}
protected virtual void DrawAdditionalEvents() {}
}
}

View File

@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: 6b59c308aff5e054991de809a3b5985b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Editors/KeepAliveBehaviourEditor.cs
uploadId: 773557

View File

@ -0,0 +1,48 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode.Editors
{
using Detectors;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof (ObscuredCheatingDetector))]
internal class ObscuredCheatingDetectorEditor : KeepAliveBehaviourEditor<ObscuredCheatingDetector>
{
private SerializedProperty honeyPot;
private SerializedProperty doubleEpsilon;
private SerializedProperty floatEpsilon;
private SerializedProperty vector2Epsilon;
private SerializedProperty vector3Epsilon;
private SerializedProperty quaternionEpsilon;
protected override void FindUniqueDetectorProperties()
{
honeyPot = serializedObject.FindProperty("honeyPot");
doubleEpsilon = serializedObject.FindProperty("doubleEpsilon");
floatEpsilon = serializedObject.FindProperty("floatEpsilon");
vector2Epsilon = serializedObject.FindProperty("vector2Epsilon");
vector3Epsilon = serializedObject.FindProperty("vector3Epsilon");
quaternionEpsilon = serializedObject.FindProperty("quaternionEpsilon");
}
protected override bool DrawUniqueDetectorProperties()
{
DrawHeader("Specific settings");
EditorGUILayout.PropertyField(honeyPot);
EditorGUILayout.PropertyField(doubleEpsilon);
EditorGUILayout.PropertyField(floatEpsilon);
EditorGUILayout.PropertyField(vector2Epsilon, new GUIContent("Vector2 Epsilon"));
EditorGUILayout.PropertyField(vector3Epsilon, new GUIContent("Vector3 Epsilon"));
EditorGUILayout.PropertyField(quaternionEpsilon);
return true;
}
}
}

View File

@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: 8df36b70906d65d43bcb3ddce3fe36ba
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Editors/ObscuredCheatingDetectorEditor.cs
uploadId: 773557

View File

@ -0,0 +1,84 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode.Editors
{
using Detectors;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEngine;
using EditorTools = EditorCode.EditorTools;
[CustomEditor(typeof (SpeedHackDetector))]
internal class SpeedHackDetectorEditor : KeepAliveBehaviourEditor<SpeedHackDetector>
{
private SerializedProperty interval;
private SerializedProperty threshold;
private SerializedProperty maxFalsePositives;
private SerializedProperty coolDown;
private SerializedProperty useDsp;
private SerializedProperty watchTimeScale;
protected override void FindUniqueDetectorProperties()
{
interval = serializedObject.FindProperty("interval");
threshold = serializedObject.FindProperty("threshold");
maxFalsePositives = serializedObject.FindProperty("maxFalsePositives");
coolDown = serializedObject.FindProperty("coolDown");
useDsp = serializedObject.GetProperty(nameof(SpeedHackDetector.UseDsp));
watchTimeScale = serializedObject.GetProperty(nameof(SpeedHackDetector.WatchTimeScale));
}
protected override bool DrawUniqueDetectorProperties()
{
DrawHeader("Specific settings");
EditorGUILayout.PropertyField(interval);
EditorGUILayout.PropertyField(threshold);
EditorGUILayout.PropertyField(maxFalsePositives);
EditorGUILayout.PropertyField(coolDown);
#if UNITY_2020_1_OR_NEWER
EditorGUILayout.PropertyField(useDsp);
#else
EditorGUILayout.PropertyField(useDsp, new GUIContent( ObjectNames.NicifyVariableName(nameof(SpeedHackDetector.UseDsp)), useDsp.tooltip));
#endif
if (useDsp.boolValue)
EditorGUILayout.HelpBox("Dsp timers may cause false positives on some hardware.\nMake sure to test on target devices before using this in production.", MessageType.Warning);
#if UNITY_AUDIO_MODULE
if (!EditorTools.IsAudioManagerEnabled())
{
EditorGUILayout.HelpBox("Dsp option is not available since Disable Unity Audio option is enabled.", MessageType.Error);
if (GUILayout.Button("Open Audio Settings"))
{
SettingsService.OpenProjectSettings("Project/Audio");
#if UNITY_2021_3_OR_NEWER
Highlighter.Highlight("Project Settings", EditorTools.GetAudioManagerEnabledPropertyPath(), HighlightSearchMode.Identifier);
#endif
}
}
#else
EditorGUILayout.HelpBox("Dsp option is not available since built-in Audio module is disabled.", MessageType.Error);
#endif
#if UNITY_2020_1_OR_NEWER
EditorGUILayout.PropertyField(watchTimeScale);
#else
EditorGUILayout.PropertyField(watchTimeScale, new GUIContent(ObjectNames.NicifyVariableName(nameof(SpeedHackDetector.WatchTimeScale)), watchTimeScale.tooltip));
#endif
if (watchTimeScale.boolValue)
{
EditorGUILayout.HelpBox("TimeScale watching monitors for unauthorized changes to Time.timeScale.\n" +
"Use SpeedHackDetector.SetTimeScale and AllowAnyTimeScale APIs to change timeScale safely.", MessageType.Info);
}
return true;
}
}
}

View File

@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: 199f39969debe8f46afc5f32b333be3b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Editors/SpeedHackDetectorEditor.cs
uploadId: 773557

View File

@ -0,0 +1,74 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode.Editors
{
using Detectors;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(TimeCheatingDetector))]
internal class TimeCheatingDetectorEditor : KeepAliveBehaviourEditor<TimeCheatingDetector>
{
#if !ACTK_PREVENT_INTERNET_PERMISSION
private SerializedProperty requestUrl;
private SerializedProperty requestMethod;
private SerializedProperty timeoutSeconds;
private SerializedProperty interval;
private SerializedProperty realCheatThreshold;
private SerializedProperty wrongTimeThreshold;
private SerializedProperty ignoreSetCorrectTime;
protected override void FindUniqueDetectorProperties()
{
requestUrl = serializedObject.FindProperty("requestUrl");
requestMethod = serializedObject.FindProperty("requestMethod");
timeoutSeconds = serializedObject.FindProperty("timeoutSeconds");
interval = serializedObject.FindProperty("interval");
realCheatThreshold = serializedObject.FindProperty("realCheatThreshold");
wrongTimeThreshold = serializedObject.FindProperty("wrongTimeThreshold");
ignoreSetCorrectTime = serializedObject.FindProperty("ignoreSetCorrectTime");
}
protected override bool DrawUniqueDetectorProperties()
{
DrawHeader("Specific settings");
EditorGUIUtility.labelWidth += 10;
EditorGUILayout.PropertyField(ignoreSetCorrectTime);
EditorGUIUtility.labelWidth -= 10;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(requestUrl, new GUIContent("URL", requestUrl.tooltip));
if (EditorGUI.EndChangeCheck())
{
self.RequestUrl = requestUrl.stringValue;
}
#if UNITY_WEBGL
GUILayout.Label("<b>To avoid CORS limitations while running in WebGL, URL will be changed to the current domain, if it does points to any other domain</b>", GUITools.RichMiniLabel);
EditorGUILayout.Space();
#endif
EditorGUILayout.PropertyField(requestMethod, new GUIContent("Method", requestMethod.tooltip));
EditorGUILayout.PropertyField(timeoutSeconds);
EditorGUILayout.PropertyField(interval);
EditorGUILayout.PropertyField(realCheatThreshold);
EditorGUILayout.PropertyField(wrongTimeThreshold);
return true;
}
#else
protected override bool DrawUniqueDetectorProperties()
{
GUILayout.Label("<b>Detector disabled with ACTK_PREVENT_INTERNET_PERMISSION conditional symbol</b>", GUITools.RichLabel);
return true;
}
#endif
}
}

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 2fb4c7beaf58d554ab0f85d86b53726e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Editors/TimeCheatingDetectorEditor.cs
uploadId: 773557

View File

@ -0,0 +1,139 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode.Editors
{
using Detectors;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof (WallHackDetector))]
internal class WallHackDetectorEditor : KeepAliveBehaviourEditor<WallHackDetector>
{
private SerializedProperty wireframeDelay;
private SerializedProperty raycastDelay;
private SerializedProperty spawnPosition;
private SerializedProperty maxFalsePositives;
private SerializedProperty checkRigidbody;
private SerializedProperty checkController;
private SerializedProperty checkWireframe;
private SerializedProperty checkRaycast;
protected override void FindUniqueDetectorProperties()
{
raycastDelay = serializedObject.FindProperty("raycastDelay");
wireframeDelay = serializedObject.FindProperty("wireframeDelay");
spawnPosition = serializedObject.FindProperty("spawnPosition");
maxFalsePositives = serializedObject.FindProperty("maxFalsePositives");
checkRigidbody = serializedObject.FindProperty("checkRigidbody");
checkController = serializedObject.FindProperty("checkController");
checkWireframe = serializedObject.FindProperty("checkWireframe");
checkRaycast = serializedObject.FindProperty("checkRaycast");
}
protected override bool DrawUniqueDetectorProperties()
{
var detector = self;
if (detector == null) return false;
DrawHeader("Specific settings");
if (PropertyFieldChanged(checkRigidbody, new GUIContent("Rigidbody")))
{
detector.CheckRigidbody = checkRigidbody.boolValue;
}
if (PropertyFieldChanged(checkController, new GUIContent("Character Controller")))
{
detector.CheckController = checkController.boolValue;
}
if (PropertyFieldChanged(checkWireframe, new GUIContent("Wireframe")))
{
detector.CheckWireframe = checkWireframe.boolValue;
}
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(wireframeDelay, new GUIContent("Delay"));
EditorGUI.indentLevel--;
if (PropertyFieldChanged(checkRaycast, new GUIContent("Raycast")))
{
detector.CheckRaycast = checkRaycast.boolValue;
}
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(raycastDelay, new GUIContent("Delay"));
EditorGUI.indentLevel--;
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(spawnPosition);
if (Vector3.Distance(spawnPosition.vector3Value, Vector3.zero) <= 0.001f)
{
EditorGUILayout.HelpBox("Please consider placing spawn position as far from your moving objects as possible to avoid false positives", MessageType.Warning);
EditorGUILayout.Space();
}
EditorGUILayout.PropertyField(maxFalsePositives);
EditorGUILayout.Separator();
if (checkWireframe.boolValue && !SettingsGUI.IsWallhackDetectorShaderIncluded())
{
using (GUITools.Vertical(GUITools.PanelWithBackground))
{
EditorGUILayout.Separator();
EditorGUILayout.HelpBox("Wallhack Detector shader for Wireframe checks is not included into the build! Detector may work incorrectly",
MessageType.Error, true);
if (GUILayout.Button("Include in Settings..."))
{
ACTkSettings.Show();
}
EditorGUILayout.Separator();
}
}
if (checkRaycast.boolValue || checkController.boolValue || checkRigidbody.boolValue)
{
var layerId = LayerMask.NameToLayer("Ignore Raycast");
if (Physics.GetIgnoreLayerCollision(layerId, layerId))
{
EditorGUILayout.LabelField("IgnoreRaycast physics layer should collide with itself to avoid false positives! See readme's troubleshooting section for details.", EditorStyles.wordWrappedLabel);
if (GUILayout.Button("Edit in Physics settings"))
{
SettingsService.OpenProjectSettings("Project/Physics");
}
}
}
return true;
}
private static bool PropertyFieldChanged(SerializedProperty property, GUIContent content, params GUILayoutOption[] options)
{
var result = false;
EditorGUI.BeginChangeCheck();
if (content == null)
{
EditorGUILayout.PropertyField(property, options);
}
else
{
EditorGUILayout.PropertyField(property, content, options);
}
if (EditorGUI.EndChangeCheck())
{
result = true;
}
return result;
}
}
}

View File

@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: 2f88f8bd1134b464fa26c27d9979e6db
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Editors/WallHackDetectorEditor.cs
uploadId: 773557

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b60780b31b4fac0448812d7db515e8a7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0eab0a2d46900f5498873b929a503bad
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7ae78be353f2d8243b15efd15919c5f5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,203 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System.Linq;
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using CodeStage.AntiCheat.Common;
using CodeStage.AntiCheat.Genuine.CodeHash;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using Debug = UnityEngine.Debug;
namespace CodeStage.AntiCheat.EditorCode.PostProcessors
{
/// <summary>
/// Does calculates code hash after build if you use option "Generate code hash".
/// Listen to HashesGenerated or look for hash for each build in the Editor Console.
/// </summary>
/// Resulting hash in most cases should match value you get from the \ref CodeStage.AntiCheat.Genuine.CodeHash.CodeHashGenerator "CodeHashGenerator"
public class CodeHashGeneratorPostprocessor : IPostprocessBuildWithReport
{
/// <summary>
/// Equals int.MaxValue to make sure this postprocessor will run as late as possible
/// so you could run own postprocessors before and subscribe to HashesGenerated event.
/// </summary>
/// Used at CodeHashGeneratorListener example.
public const int CallbackOrder = int.MaxValue;
/// <summary>
/// HashesGenerated event delegate.
/// </summary>
/// <param name="report">Standard post-build report from Unity.</param>
/// <param name="hashedBuilds">Build hashing results array.</param>
///
/// You may generate multiple actual builds within single build operation,
/// like multiple APKs when you use "Split APKs by target architecture" option,
/// so you may have more than one valid hashed builds for one actual build procedure.
public delegate void OnHashesGenerated(BuildReport report, IReadOnlyList<BuildHashes> hashedBuilds);
/// <summary>
/// You may listen to this event if you wish to post-process resulting code hash,
/// e.g. upload it to the server for the later runtime check with CodeHashGenerator.
/// </summary>
/// Make sure to enable "Generate code hash on build completion" option in the ACTk settings to make this event work.
public static event OnHashesGenerated HashesGenerated;
int IOrderedCallback.callbackOrder => CallbackOrder;
async void IPostprocessBuildWithReport.OnPostprocessBuild(BuildReport report)
{
if (!ACTkSettings.Instance.PreGenerateBuildHash || !CodeHashGenerator.IsTargetPlatformCompatible())
return;
await CalculateBuildReportHashesAsync(report, true, true);
}
/// <summary>
/// Calls selection dialog and calculates hashes for the selected build.
/// </summary>
/// <param name="buildPath">Path to the .apk / .aab or .exe file. Pass null to show file selection dialog.</param>
/// <param name="printToConsole">Path to the .apk / .aab or .exe file. Pass null to show file selection dialog.</param>
/// <returns>Valid BuildHashes instance or null in case of error / user cancellation.</returns>
public static BuildHashes CalculateExternalBuildHashes(string buildPath, bool printToConsole)
{
return AsyncHelpers.RunSync(() => CalculateExternalBuildHashesAsync(buildPath, printToConsole));
}
/// <summary>
/// Calls selection dialog and calculates hashes for the selected build.
/// </summary>
/// <param name="buildPath">Path to the .apk / .aab or .exe file. Pass null to show file selection dialog.</param>
/// <param name="printToConsole">Path to the .apk / .aab or .exe file. Pass null to show file selection dialog.</param>
/// <returns>Task with Valid BuildHashes instance or null in case of error / user cancellation.</returns>
public static async Task<BuildHashes> CalculateExternalBuildHashesAsync(string buildPath, bool printToConsole)
{
if (buildPath == null)
{
buildPath = EditorUtility.OpenFilePanel(
"Select Standalone Windows build exe or Android build apk / aab", "", "exe,apk,aab");
if (string.IsNullOrEmpty(buildPath))
{
Debug.Log(ACTk.LogPrefix + "Hashing cancelled by user.");
return null;
}
}
var extension = Path.GetExtension(buildPath);
if (string.IsNullOrEmpty(extension))
return null;
extension = extension.ToLower(CultureInfo.InvariantCulture);
try
{
EditorUtility.DisplayProgressBar("ACTk: Generating code hash", "Preparing...", 0);
BuildHashes hashes;
if (extension == ".apk" || extension == ".aab")
hashes = (await AndroidBuildHashGenerator.GetBuildHashes(new[] { buildPath })).FirstOrDefault();
else
hashes = (await WindowsBuildHashGenerator.GetBuildHashes(buildPath)).FirstOrDefault();
if (printToConsole)
hashes?.PrintToConsole();
return hashes;
}
catch (Exception e)
{
ACTk.PrintExceptionForSupport("Error while trying to hash build!", e);
}
finally
{
EditorUtility.ClearProgressBar();
}
return null;
}
/// <summary>
/// Calculates hashes for the given build report. Can be useful if you wish to hash build you just made with BuildPipeline.
/// </summary>
/// Will not trigger the HashesGenerated event.
/// <param name="report">BuildReport you wish to calculates hashes for</param>
/// <param name="printToConsole">Specifies if calculated hashes should be printed to Unity Console</param>
/// <returns>Readonly List of the BuildHashes, one per each resulting build from the BuildReport.</returns>
public static Task<IReadOnlyList<BuildHashes>> CalculateBuildReportHashesAsync(BuildReport report,
bool printToConsole)
{
return CalculateBuildReportHashesAsync(report, false, printToConsole);
}
private static async Task<IReadOnlyList<BuildHashes>> CalculateBuildReportHashesAsync(BuildReport report,
bool triggerEvent, bool printLogs)
{
if (EditorUserBuildSettings.GetPlatformSettings(report.summary.platformGroup.ToString(),
"CreateSolution") == "true")
{
Debug.Log(
ACTk.LogPrefix + "Build hashing is skipped due to the 'Create Visual Studio Solution' option.");
return null;
}
try
{
EditorUtility.DisplayProgressBar("ACTk: Generating code hash", "Preparing...", 0);
var hashedBuilds = await GetHashedBuilds(report);
if (hashedBuilds == null || hashedBuilds.Count == 0)
{
Debug.Log(ACTk.LogPrefix + "Couldn't pre-generate code hash. " +
"Please run your build and generate hash with CodeHashGenerator.");
return null;
}
if (printLogs)
{
foreach (var hashedBuild in hashedBuilds)
{
hashedBuild.PrintToConsole();
}
}
if (triggerEvent)
HashesGenerated?.Invoke(report, hashedBuilds);
return hashedBuilds;
}
catch (Exception e)
{
ACTk.PrintExceptionForSupport("Error while trying to hash build!", e);
}
finally
{
EditorUtility.ClearProgressBar();
}
return null;
}
private static async Task<IReadOnlyList<BuildHashes>> GetHashedBuilds(BuildReport report)
{
var platform = report.summary.platform;
switch (platform)
{
case BuildTarget.StandaloneWindows64:
case BuildTarget.StandaloneWindows:
return await WindowsBuildHashGenerator.GetBuildHashes(report.summary.outputPath);
case BuildTarget.Android:
return await AndroidBuildHashGenerator.GetBuildHashes(report);
default:
return null;
}
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c82c021ded9a49b44b3701f1859341cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Genuine/CodeHash/PostProcess/CodeHashGeneratorPostprocessor.cs
uploadId: 773557

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 881ba0c65bee45139ca757895221c6ab
timeCreated: 1680806941

View File

@ -0,0 +1,184 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CodeStage.AntiCheat.Common;
using CodeStage.AntiCheat.EditorCode.ICSharpCode.SharpZipLib.Zip;
using CodeStage.AntiCheat.Genuine.CodeHash;
using CodeStage.AntiCheat.Utils;
using UnityEditor;
using UnityEditor.Build.Reporting;
using Debug = UnityEngine.Debug;
namespace CodeStage.AntiCheat.EditorCode.PostProcessors
{
internal static class AndroidBuildHashGenerator
{
private static readonly object ProgressLock = new object();
public static async Task<BuildHashes[]> GetBuildHashes(BuildReport report)
{
if ((report.summary.options & BuildOptions.PatchPackage) != 0)
{
Debug.Log(ACTk.LogPrefix + "Patch hashing is skipped, only full build hashing is supported.");
return Array.Empty<BuildHashes>();
}
#if UNITY_2022_1_OR_NEWER
var files = report.GetFiles();
#else
var files = report.files;
#endif
var filePaths = new string[files.Length];
for (var i = 0; i < filePaths.Length; i++)
{
filePaths[i] = files[i].path;
}
return await GetBuildHashes(filePaths);
}
public static async Task<BuildHashes[]> GetBuildHashes(string[] files)
{
var result = new List<BuildHashes>();
foreach (var path in files)
{
var extension = Path.GetExtension(path);
if (!string.IsNullOrEmpty(extension))
extension = extension.ToLower(CultureInfo.InvariantCulture);
if (extension != ".apk" && extension != ".aab")
continue;
var progress = FilesProgress.CreateNew("ACTk: Generating code hash");
var filters = GetFilters();
var buildHashes = await Task.Run(() => HashSuitableFilesInZipFile(path, filters,
progress));
if (buildHashes != null)
result.Add(buildHashes);
}
if (result.Count == 0)
{
if (!EditorUserBuildSettings.exportAsGoogleAndroidProject)
ACTk.PrintExceptionForSupport("Couldn't find compiled APK or AAB build!");
else
Debug.LogWarning("Couldn't find compiled APK or AAB build! " +
"This is fine if you use Export Project feature.");
}
return result.ToArray();
}
private static BuildHashes HashSuitableFilesInZipFile(string path, FilteringData filters,
IProgress<FilesProgress> progress)
{
ZipFile zf = null;
try
{
var sw = Stopwatch.StartNew();
var latestPercent = 0;
var filesChecked = 0;
var fileHashes = new ConcurrentBag<FileHash>();
var fs = File.OpenRead(path);
zf = new ZipFile(fs);
var count = zf.Count;
var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
var sha1Pool = new ThreadSafeDisposablesPool<SHA1Wrapper>(() => new SHA1Wrapper());
Parallel.ForEach(zf.Cast<ZipEntry>(), options, zipEntry =>
{
string entryFileName = null;
var skipped = true;
try
{
// skip folders since we can't hash them
if (!zipEntry.IsFile)
return;
entryFileName = zipEntry.Name;
if (filters.IsIgnored(entryFileName))
return;
if (!filters.IsIncluded(entryFileName))
return;
using (var zipStream = zf.GetInputStream(zipEntry))
{
fileHashes.Add(new FileHash(entryFileName, zipStream, sha1Pool));
}
skipped = false;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
lock (ProgressLock)
{
filesChecked++;
if (!skipped)
progress?.ReportPercent(ref latestPercent, filesChecked, count, Path.GetFileName(entryFileName));
}
}
});
sha1Pool.Dispose();
progress?.Report(FilesProgress.None());
sw.Stop();
if (fileHashes.Count == 0)
return null;
var result = new BuildHashes(path, fileHashes.ToArray())
{
DurationSeconds = sw.Elapsed.TotalSeconds
};
return result;
}
catch (Exception e)
{
ACTk.PrintExceptionForSupport("Error while calculating code hash!", e);
}
finally
{
if (zf != null)
{
zf.IsStreamOwner = true;
zf.Close();
}
}
return null;
}
private static FilteringData GetFilters()
{
var il2Cpp = false;
#if UNITY_EDITOR
il2Cpp = SettingsUtils.IsIL2CPPEnabled();
#elif ENABLE_IL2CPP
il2Cpp = true;
#endif
return FiltersProducer.GetFileFiltersAndroid(il2Cpp);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 9717d52c2bf842caaa8575f8f2947afa
timeCreated: 1680806503
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Genuine/CodeHash/PostProcess/Platforms/AndroidBuildHashGenerator.cs
uploadId: 773557

View File

@ -0,0 +1,49 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using CodeStage.AntiCheat.Common;
using CodeStage.AntiCheat.Genuine.CodeHash;
namespace CodeStage.AntiCheat.EditorCode.PostProcessors
{
internal static class WindowsBuildHashGenerator
{
public static async Task<IReadOnlyList<BuildHashes>> GetBuildHashes(string buildPath)
{
var folder = Path.GetDirectoryName(buildPath);
if (folder == null)
{
ACTk.PrintExceptionForSupport("Could not found build folder for this file: " + buildPath);
return Array.Empty<BuildHashes>();
}
var progress = FilesProgress.CreateNew("ACTk: Generating code hash");
var filters = GetFilters();
var buildHashes = await Task.Run(()=> StandaloneWindowsWorker.GetBuildHashes(folder, filters, Environment.ProcessorCount,
progress));
if (buildHashes == null)
return Array.Empty<BuildHashes>();
return new [] { buildHashes };
}
private static FilteringData GetFilters()
{
var il2Cpp = false;
#if UNITY_EDITOR
il2Cpp = SettingsUtils.IsIL2CPPEnabled();
#elif ENABLE_IL2CPP
il2Cpp = true;
#endif
return FiltersProducer.GetFileFiltersStandaloneWindows(il2Cpp);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 02b38fd3639d4fd7a1a6732fadbeebc8
timeCreated: 1680806778
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Genuine/CodeHash/PostProcess/Platforms/WindowsBuildHashGenerator.cs
uploadId: 773557

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59e19c7338f04be697d7788c76f0cde6
timeCreated: 1552927805

View File

@ -0,0 +1,70 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode
{
using System;
/// <summary>
/// Describes assembly which is added to the InjectionDetector "white list".
/// </summary>
[Serializable]
public class AllowedAssembly
{
[Obsolete("Please use Name property instead.", false)]
public string name => Name;
[Obsolete("Please use Hashes property instead.", false)]
public int[] hashes => Hashes;
/// <summary>
/// Assembly name, i.e.: ACTk.Runtime.
/// </summary>
public string Name { get; }
/// <summary>
/// Array of whitelisted hashes for the assembly with given Name.
/// </summary>
public int[] Hashes { get; private set; }
/// <summary>
/// Constructs new instance.
/// </summary>
/// <param name="name">Sets Name property.</param>
/// <param name="hashes">Sets Hashes property.</param>
public AllowedAssembly(string name, int[] hashes)
{
Name = name;
Hashes = hashes;
}
/// <summary>
/// Allows adding new hash to the Hashes collection.
/// </summary>
/// <param name="hash">New whitelisted hash for the assembly with specified Name.</param>
/// <returns>True if hash was added and false otherwise (i.e. when hash already existed in the collection).</returns>
public bool AddHash(int hash)
{
if (Array.IndexOf(Hashes, hash) != -1) return false;
var oldLen = Hashes.Length;
var newLen = oldLen + 1;
var newHashesArray = new int[newLen];
Array.Copy(Hashes, newHashesArray, oldLen);
Hashes = newHashesArray;
Hashes[oldLen] = hash;
return true;
}
public override string ToString()
{
return Name + " (hashes: " + Hashes.Length + ")";
}
}
}

View File

@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: 99e59ee643091664d874b8418be8b78e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Injection/AllowedAssembly.cs
uploadId: 773557

View File

@ -0,0 +1,23 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode
{
using System.IO;
internal static class InjectionConstants
{
public const string LegacyWhitelistRelativePath = "InjectionDetectorData/UserWhitelist.bytes";
public const string PrefsKey = "ACTDIDEnabledGlobal";
public const string DataSeparator = ":";
public static readonly string ResourcesFolder = Path.Combine(ACTkEditorConstants.AssetsFolder, "Resources");
public static readonly string DataFilePath = Path.Combine(ResourcesFolder, DataFileName);
private const string DataFileName = "fndid.bytes";
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fdb8abbb3fde4da3aa210fccf74e9524
timeCreated: 1552925592
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Injection/InjectionConstants.cs
uploadId: 773557

View File

@ -0,0 +1,229 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
#if (UNITY_STANDALONE || UNITY_ANDROID)
#define UNITY_SUPPORTED_PLATFORM
#endif
#define ACTK_DEBUG
#undef ACTK_DEBUG
#define ACTK_DEBUG_VERBOSE
#undef ACTK_DEBUG_VERBOSE
#define ACTK_DEBUG_PARANIOD
#undef ACTK_DEBUG_PARANIOD
#if ACTK_DEBUG_PARANIOD
#define ACTK_DEBUG
#define ACTK_DEBUG_VERBOSE
#endif
#if ACTK_DEBUG_VERBOSE
#define ACTK_DEBUG
#endif
namespace CodeStage.AntiCheat.EditorCode
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Common;
using ObscuredTypes;
using UnityEditor;
using UnityEngine;
internal static class InjectionRoutines
{
private static bool cleanupDone;
public static bool IsInjectionPossible()
{
return IsTargetPlatformCompatible() && !SettingsUtils.IsIL2CPPEnabled();
}
public static bool IsTargetPlatformCompatible()
{
#if UNITY_SUPPORTED_PLATFORM
return true;
#else
return false;
#endif
}
public static int GetAssemblyHash(AssemblyName ass)
{
var hashInfo = ass.Name;
var bytes = ass.GetPublicKeyToken();
if (bytes != null && bytes.Length == 8)
{
hashInfo += PublicKeyTokenToString(bytes);
}
// Jenkins hash function (http://en.wikipedia.org/wiki/Jenkins_hash_function)
var result = 0;
var len = hashInfo.Length;
for (var i = 0; i < len; ++i)
{
result += hashInfo[i];
result += (result << 10);
result ^= (result >> 6);
}
result += (result << 3);
result ^= (result >> 11);
result += (result << 15);
return result;
}
public static List<AllowedAssembly> MergeAllowedAssemblies(IEnumerable<AllowedAssembly> collection1, IEnumerable<AllowedAssembly> collection2)
{
var result = new List<AllowedAssembly>(collection1);
foreach (var whiteListedAssembly in collection2)
{
var exists = false;
foreach (var assembly in result)
{
if (assembly.Name != whiteListedAssembly.Name) continue;
exists = true;
foreach (var hash in whiteListedAssembly.Hashes)
{
if (Array.IndexOf(assembly.Hashes, hash) == -1)
{
assembly.AddHash(hash);
}
}
break;
}
if (!exists)
{
result.Add(whiteListedAssembly);
}
}
return result;
}
public static void InitCleanup()
{
cleanupDone = false;
}
public static void Cleanup()
{
if (cleanupDone) return;
cleanupDone = true;
TryMigrateSettings();
TryMigrateLegacyInjectionWhitelist();
if (File.Exists(InjectionConstants.DataFilePath))
{
#if ACTK_DEBUG
Debug.Log(ACTk.LogPrefix + "Data file found and going to be removed: " + InjectionConstants.DataFilePath);
#endif
EditorTools.DeleteFile(InjectionConstants.DataFilePath);
EditorTools.DeleteFile(AssetDatabase.GetTextMetaFilePathFromAssetPath(InjectionConstants.DataFilePath));
EditorTools.RemoveDirectoryIfEmpty(InjectionConstants.ResourcesFolder);
AssetDatabase.Refresh();
}
}
private static string PublicKeyTokenToString(byte[] bytes)
{
var result = "";
// AssemblyName.GetPublicKeyToken() returns 8 bytes
for (var i = 0; i < 8; i++)
{
result += ACTkEditorConstants.HexTable[bytes[i]];
}
return result;
}
private static void TryMigrateSettings()
{
if (!EditorPrefs.HasKey(InjectionConstants.PrefsKey)) return;
ACTkSettings.Instance.InjectionDetectorEnabled = EditorPrefs.GetBool(InjectionConstants.PrefsKey);
EditorPrefs.DeleteKey(InjectionConstants.PrefsKey);
}
private static void TryMigrateLegacyInjectionWhitelist()
{
if (!File.Exists(InjectionConstants.LegacyWhitelistRelativePath)) return;
var whitelistedAssemblies = LoadAndParseLegacyWhitelist();
ACTkSettings.Instance.InjectionDetectorWhiteList = MergeAllowedAssemblies(ACTkSettings.Instance.InjectionDetectorWhiteList, whitelistedAssemblies);
}
private static IEnumerable<AllowedAssembly> LoadAndParseLegacyWhitelist()
{
var result = new List<AllowedAssembly>();
string[] separator = { InjectionConstants.DataSeparator };
var fs = new FileStream(InjectionConstants.LegacyWhitelistRelativePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var br = new BinaryReader(fs);
try
{
var count = br.ReadInt32();
for (var i = 0; i < count; i++)
{
var line = br.ReadString();
line = new string(ObscuredString.Encrypt(line, ACTk.StringKey));
var strArr = line.Split(separator, StringSplitOptions.RemoveEmptyEntries);
var stringsCount = strArr.Length;
if (stringsCount > 1)
{
var assemblyName = strArr[0];
var hashes = new int[stringsCount - 1];
for (var j = 1; j < stringsCount; j++)
{
var success = int.TryParse(strArr[j], out var parseResult);
if (success)
{
hashes[j - 1] = parseResult;
}
else
{
Debug.LogError(ACTk.LogPrefix + "Could not parse value: " + strArr[j] +
", line:\n" + line);
}
}
result.Add(new AllowedAssembly(assemblyName, hashes));
}
else
{
throw new Exception("Error parsing whitelist file line!");
}
}
}
catch (Exception e)
{
ACTk.PrintExceptionForSupport("Error while reading legacy whitelist!", e);
}
finally
{
br.Close();
fs.Close();
}
return result;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 682e4f8bd25b4b3f9ab90c2c080decf5
timeCreated: 1552927820
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Injection/InjectionRoutines.cs
uploadId: 773557

View File

@ -0,0 +1,234 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
#define ACTK_DEBUG
#undef ACTK_DEBUG
#define ACTK_DEBUG_VERBOSE
#undef ACTK_DEBUG_VERBOSE
#define ACTK_DEBUG_PARANIOD
#undef ACTK_DEBUG_PARANIOD
#if ACTK_DEBUG_PARANIOD
#define ACTK_DEBUG
#define ACTK_DEBUG_VERBOSE
#endif
#if ACTK_DEBUG_VERBOSE
#define ACTK_DEBUG
#endif
namespace CodeStage.AntiCheat.EditorCode
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Common;
using Detectors;
using ObscuredTypes;
using PostProcessors;
using UnityEditor;
using Debug = UnityEngine.Debug;
#if ACTK_DEBUG
using System.Diagnostics;
#endif
internal static class InjectionWhitelistBuilder
{
private const string ProgressCaption = "ACTk: Building InjectionDetector Whitelist";
#if ACTK_DEBUG
private static Stopwatch sw;
#endif
public static void GenerateWhitelist()
{
try
{
GenerateWhitelistInternal();
}
catch (Exception e)
{
ACTk.PrintExceptionForSupport($"Something went wrong while building {nameof(InjectionDetector)} whitelist!", e);
}
finally
{
EditorUtility.ClearProgressBar();
}
}
private static void GenerateWhitelistInternal()
{
#if ACTK_DEBUG
sw = Stopwatch.StartNew();
sw.Stop();
Debug.Log("=== Injection Detector Whitelist Build Start ===");
sw.Start();
#endif
EditorUtility.DisplayProgressBar(ProgressCaption, "Gathering assemblies", 0);
var assembliesInBuild = GetAssembliesInBuild();
if (assembliesInBuild.Length == 0)
ACTk.PrintExceptionForSupport("Can't find any assemblies in build!");
var assembliesAllowedByUser = GetUserWhiteListAssemblies();
var allAllowedAssemblies = InjectionRoutines.MergeAllowedAssemblies(assembliesInBuild, assembliesAllowedByUser);
EditorUtility.DisplayProgressBar(ProgressCaption, "Writing assemblies hashes", 0);
WriteAllowedAssemblies(allAllowedAssemblies);
#if ACTK_DEBUG
sw.Stop();
Debug.Log(ACTk.LogPrefix + "WhiteList build duration: " + sw.ElapsedMilliseconds + " ms.");
#endif
AssetDatabase.Refresh();
}
private static AllowedAssembly[] GetAssembliesInBuild()
{
#if ACTK_DEBUG_VERBOSE
sw.Stop();
Debug.Log(ACTk.LogPrefix + "Trying to guess which assemblies can get into the build...");
sw.Start();
#endif
var libraries = BuildPostProcessor.GetGuessedLibrariesForBuild();
#if ACTK_DEBUG_VERBOSE
sw.Stop();
Debug.Log(ACTk.LogPrefix + "Total libraries candidates: " + libraries.Length);
sw.Start();
var invalidAssemblies = string.Empty;
#endif
var result = new List<AllowedAssembly>();
foreach (var libraryPath in libraries)
{
#if ACTK_DEBUG_PARANIOD
sw.Stop();
Debug.Log(ACTk.LogPrefix + "Checking library at the path: " + libraryPath);
sw.Start();
#endif
try
{
var assName = AssemblyName.GetAssemblyName(libraryPath);
var name = assName.Name;
var hash = InjectionRoutines.GetAssemblyHash(assName);
var allowed = result.FirstOrDefault(allowedAssembly => allowedAssembly.Name == name);
if (allowed != null)
{
allowed.AddHash(hash);
}
else
{
allowed = new AllowedAssembly(name, new[] { hash });
result.Add(allowed);
}
}
catch
{
// not a valid IL assembly, skipping
#if ACTK_DEBUG_VERBOSE
invalidAssemblies += libraryPath + "\n";
#endif
}
}
#if ACTK_DEBUG_VERBOSE
if (!string.IsNullOrEmpty(invalidAssemblies))
{
sw.Stop();
Debug.Log(ACTk.LogPrefix + "Not valid assemblies:\n" + invalidAssemblies);
sw.Start();
}
#endif
#if ACTK_DEBUG
sw.Stop();
var trace = ACTk.LogPrefix + "Found assemblies in build (" + result.Count + ", " + sw.ElapsedMilliseconds + " ms):\n";
foreach (var allowedAssembly in result)
{
trace += " Name: " + allowedAssembly.name + "\n";
trace = allowedAssembly.hashes.Aggregate(trace, (current, hash) => current + (" Hash: " + hash + "\n"));
}
Debug.Log(trace);
sw.Start();
#endif
return result.ToArray();
}
private static AllowedAssembly[] GetUserWhiteListAssemblies()
{
var userWhiteList = ACTkSettings.Instance.InjectionDetectorWhiteList;
#if ACTK_DEBUG
sw.Stop();
var trace = ACTk.LogPrefix + "User White List assemblies (" + userWhiteList.Count + "):\n";
foreach (var allowedAssembly in userWhiteList)
{
trace += " Name: " + allowedAssembly.name + "\n";
trace = allowedAssembly.hashes.Aggregate(trace, (current, hash) => current + (" Hash: " + hash + "\n"));
}
Debug.Log(trace);
sw.Start();
#endif
return userWhiteList.ToArray();
}
private static void WriteAllowedAssemblies(List<AllowedAssembly> assemblies)
{
Directory.CreateDirectory(InjectionConstants.ResourcesFolder);
var bw = new BinaryWriter(new FileStream(InjectionConstants.DataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read), Encoding.Unicode);
bw.Write(assemblies.Count);
#if ACTK_DEBUG_VERBOSE
sw.Stop();
Debug.Log(ACTk.LogPrefix + "Writing assemblies data, count: " + assemblies.Count);
sw.Start();
#endif
foreach (var assembly in assemblies)
{
var name = assembly.Name;
var hashes = "";
for (var j = 0; j < assembly.Hashes.Length; j++)
{
hashes += assembly.Hashes[j].ToString(CultureInfo.InvariantCulture);
if (j < assembly.Hashes.Length - 1)
{
hashes += InjectionConstants.DataSeparator;
}
}
var line = ObscuredString.Encrypt(name + InjectionConstants.DataSeparator + hashes, ACTk.StringKey);
#if ACTK_DEBUG_PARANIOD
sw.Stop();
Debug.Log(ACTk.LogPrefix + "Writing assembly:\n" + name + InjectionConstants.DataSeparator + hashes + "\n" +
new string(line) + ", length: " + line.Length);
sw.Start();
#endif
bw.Write(line.Length);
bw.Write(line, 0, line.Length);
}
bw.Close();
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 4ed4f862c6dc42a49c1f47d1e9ee2fb5
timeCreated: 1552945114
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Injection/InjectionWhitelistBuilder.cs
uploadId: 773557

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: bb730acf6c1569848b1187cd6c0e312c
folderAsset: yes
timeCreated: 1552913902
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,93 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode.PostProcessors
{
using System;
using System.IO;
using System.Text;
using Common;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
internal class BuildPostProcessor : IPreprocessBuildWithReport, IPostBuildPlayerScriptDLLs, IPostprocessBuildWithReport
{
int IOrderedCallback.callbackOrder => int.MaxValue - 1;
public void OnPreprocessBuild(BuildReport report)
{
if (!ACTkSettings.Instance.InjectionDetectorEnabled ||
!InjectionRoutines.IsInjectionPossible())
{
return;
}
InjectionRoutines.InitCleanup();
Prepare();
}
public void OnPostBuildPlayerScriptDLLs(BuildReport report)
{
if (!ACTkSettings.Instance.InjectionDetectorEnabled ||
!InjectionRoutines.IsInjectionPossible())
{
return;
}
InjectionWhitelistBuilder.GenerateWhitelist();
}
public void OnPostprocessBuild(BuildReport report)
{
Cleanup();
}
public static string[] GetGuessedLibrariesForBuild()
{
var stagingAreaFolder = Path.Combine(ACTkEditorConstants.ProjectTempFolder, "StagingArea");
return EditorTools.FindLibrariesAt(stagingAreaFolder);
}
private void Prepare()
{
try
{
EditorApplication.LockReloadAssemblies();
if (!Directory.Exists(InjectionConstants.ResourcesFolder))
{
Directory.CreateDirectory(InjectionConstants.ResourcesFolder);
}
File.WriteAllText(InjectionConstants.DataFilePath, "please remove me", Encoding.Unicode);
AssetDatabase.Refresh();
EditorApplication.update += OnEditorUpdate;
}
catch (Exception e)
{
ACTk.PrintExceptionForSupport("Injection Detector preparation failed!", e);
}
finally
{
EditorApplication.UnlockReloadAssemblies();
}
}
private void OnEditorUpdate()
{
if (!BuildPipeline.isBuildingPlayer)
Cleanup();
}
private void Cleanup()
{
InjectionRoutines.Cleanup();
EditorApplication.update -= OnEditorUpdate;
}
}
}

View File

@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: e79f56c36f349f64995908c2bc571662
timeCreated: 1552680217
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Injection/PostProcess/BuildPostProcessor.cs
uploadId: 773557

View File

@ -0,0 +1,276 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.EditorCode
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
internal class UserWhitelistEditor : EditorWindow
{
private const string InitialCustomName = "AssemblyName, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
private static List<AllowedAssembly> whitelist;
private static string whitelistPath;
private Vector2 scrollPosition;
private bool manualAssemblyWhitelisting;
private string manualAssemblyWhitelistingName = InitialCustomName;
internal static void ShowWindow()
{
EditorWindow myself = GetWindow<UserWhitelistEditor>(false, "Whitelist Editor", true);
myself.minSize = new Vector2(500, 200);
}
private void OnLostFocus()
{
manualAssemblyWhitelisting = false;
manualAssemblyWhitelistingName = InitialCustomName;
}
private void OnGUI()
{
if (whitelist == null)
{
whitelist = new List<AllowedAssembly>();
LoadAndParseWhitelist();
}
var tmpStyle = new GUIStyle(EditorStyles.largeLabel)
{
alignment = TextAnchor.MiddleCenter,
fontStyle = FontStyle.Bold
};
GUILayout.Label("User-defined Whitelist of Assemblies trusted by Injection Detector", tmpStyle);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
var whitelistUpdated = false;
var count = whitelist.Count;
if (count > 0)
{
for (var i = 0; i < count; i++)
{
var assembly = whitelist[i];
using (GUITools.Horizontal())
{
GUILayout.Label(assembly.ToString());
if (GUILayout.Button(new GUIContent("-", "Remove Assembly from Whitelist"), GUILayout.Width(30)))
{
whitelist.Remove(assembly);
whitelistUpdated = true;
break;
}
}
}
}
else
{
tmpStyle = new GUIStyle(EditorStyles.largeLabel)
{
alignment = TextAnchor.MiddleCenter
};
GUILayout.Label("- no Assemblies added so far (use buttons below to add) -", tmpStyle);
}
if (manualAssemblyWhitelisting)
{
manualAssemblyWhitelistingName = EditorGUILayout.TextField(manualAssemblyWhitelistingName);
using (GUITools.Horizontal())
{
if (GUILayout.Button("Save"))
{
try
{
if (manualAssemblyWhitelistingName.StartsWith("Cause:"))
{
throw new Exception("Please remove Cause: from the assembly name!");
}
var assName = new AssemblyName(manualAssemblyWhitelistingName.Trim());
var res = TryWhitelistAssemblyName(assName, true);
if (res != WhitelistingResult.Exists)
{
whitelistUpdated = true;
}
manualAssemblyWhitelisting = false;
manualAssemblyWhitelistingName = InitialCustomName;
}
catch (Exception e)
{
ShowNotification(new GUIContent(e.Message));
}
GUI.FocusControl("");
}
if (GUILayout.Button("Cancel"))
{
manualAssemblyWhitelisting = false;
manualAssemblyWhitelistingName = InitialCustomName;
GUI.FocusControl("");
}
}
}
EditorGUILayout.EndScrollView();
using (GUITools.Horizontal())
{
GUILayout.Space(20);
if (GUILayout.Button("Add Assembly"))
{
var assemblyPath = EditorUtility.OpenFilePanel("Choose an Assembly to add", "", "dll");
if (!string.IsNullOrEmpty(assemblyPath))
{
whitelistUpdated |= TryWhitelistAssemblies(new[] {assemblyPath}, true);
}
}
if (GUILayout.Button("Add Assemblies from Folder"))
{
var selectedFolder = EditorUtility.OpenFolderPanel("Choose a Folder with Assemblies", "", "");
if (!string.IsNullOrEmpty(selectedFolder))
{
var libraries = EditorTools.FindLibrariesAt(selectedFolder);
whitelistUpdated |= TryWhitelistAssemblies(libraries);
}
}
if (!manualAssemblyWhitelisting)
{
if (GUILayout.Button("Add Assembly manually"))
{
manualAssemblyWhitelisting = true;
}
}
if (count > 0)
{
if (GUILayout.Button("Clear"))
{
if (EditorUtility.DisplayDialog("Please confirm",
"Are you sure you wish to completely clear your Injection Detector whitelist?", "Yes", "No"))
{
whitelist.Clear();
whitelistUpdated = true;
}
}
}
GUILayout.Space(20);
}
GUILayout.Space(20);
if (whitelistUpdated)
{
WriteWhitelist();
}
}
private bool TryWhitelistAssemblies(IList<string> libraries, bool singleFile = false)
{
var added = 0;
var updated = 0;
var count = libraries.Count;
for (var i = 0; i < count; i++)
{
var libraryPath = libraries[i];
try
{
var assName = AssemblyName.GetAssemblyName(libraryPath);
var whitelistingResult = TryWhitelistAssemblyName(assName, singleFile);
switch (whitelistingResult)
{
case WhitelistingResult.Added:
added++;
break;
case WhitelistingResult.Updated:
updated++;
break;
case WhitelistingResult.Exists:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
catch
{
if (singleFile)
ShowNotification(new GUIContent("Selected file is not a valid .NET assembly!"));
}
}
if (!singleFile)
{
ShowNotification(new GUIContent("Assemblies added: " + added + ", updated: " + updated));
}
return added > 0 || updated > 0;
}
private WhitelistingResult TryWhitelistAssemblyName(AssemblyName assName, bool singleFile)
{
var result = WhitelistingResult.Exists;
var assNameString = assName.Name;
var hash = InjectionRoutines.GetAssemblyHash(assName);
var allowed = whitelist.FirstOrDefault(allowedAssembly => allowedAssembly.Name == assNameString);
if (allowed != null)
{
if (allowed.AddHash(hash))
{
if (singleFile) ShowNotification(new GUIContent("New hash added!"));
result = WhitelistingResult.Updated;
}
else
{
if (singleFile) ShowNotification(new GUIContent("Assembly already exists!"));
}
}
else
{
allowed = new AllowedAssembly(assNameString, new[] {hash});
whitelist.Add(allowed);
if (singleFile) ShowNotification(new GUIContent("Assembly added!"));
result = WhitelistingResult.Added;
}
return result;
}
private static void LoadAndParseWhitelist()
{
whitelist = ACTkSettings.Instance.InjectionDetectorWhiteList;
}
private static void WriteWhitelist()
{
ACTkSettings.Instance.InjectionDetectorWhiteList = whitelist;
}
//////////////////////////////////////
private enum WhitelistingResult : byte
{
Exists,
Added,
Updated
}
}
}

View File

@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: bd28be63d7a09224da0cac7e3178896d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/Injection/UserWhitelistEditor.cs
uploadId: 773557

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d1140a4b6184eae45916fdae3da40a10
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 071f007b436a44fc8f2b6425b606b621
timeCreated: 1666516531

View File

@ -0,0 +1,41 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System;
namespace CodeStage.AntiCheat.EditorCode
{
public static class MigrateUtils
{
[Obsolete("Use " + nameof(ObscuredTypesMigrator) + "." + nameof(ObscuredTypesMigrator.MigrateProjectAssets) + "(). " +
"This method will be removed in future versions.", true)]
public static void MigrateObscuredTypesInAssets(bool skipInteraction = false)
{
ObscuredTypesMigrator.MigrateProjectAssets(skipInteraction);
}
[Obsolete("Use " + nameof(ObscuredTypesMigrator) + "." + nameof(ObscuredTypesMigrator.MigrateProjectAssets) + "(). " +
"This method will be removed in future versions.", true)]
public static void MigrateObscuredTypesInAssets(bool fixOnly, bool skipInteraction)
{
ObscuredTypesMigrator.MigrateProjectAssets(fixOnly, skipInteraction);
}
[Obsolete("Use " + nameof(ObscuredTypesMigrator) + "." + nameof(ObscuredTypesMigrator.MigrateOpenedScenes) + "(). " +
"This method will be removed in future versions.", true)]
public static void MigrateObscuredTypesInScene(bool skipInteraction = false)
{
ObscuredTypesMigrator.MigrateOpenedScenes(skipInteraction);
}
[Obsolete("Use " + nameof(ObscuredTypesMigrator) + "." + nameof(ObscuredTypesMigrator.MigrateOpenedScenes) + "(). " +
"This method will be removed in future versions.", true)]
public static void MigrateObscuredTypesInScene(bool fixOnly, bool skipInteraction, bool skipSave = false)
{
ObscuredTypesMigrator.MigrateOpenedScenes(fixOnly, skipInteraction, skipSave);
}
}
}

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: c1c7ff960ccd8a243ad385a3fe965e9e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ObscuredTypes/Maintenance/MigrateUtils.cs
uploadId: 773557

View File

@ -0,0 +1,260 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using CodeStage.AntiCheat.ObscuredTypes.EditorCode;
using UnityEngine.SceneManagement;
namespace CodeStage.AntiCheat.EditorCode
{
using Common;
using ObscuredTypes;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
/// <summary>
/// Class with utility functions to help with ACTk migrations after updates.
/// </summary>
public static class ObscuredTypesMigrator
{
private const string ModuleName = "Obscured Types Migration";
private static bool fixOnlyMode = false;
private static readonly string[] TypesToMigrate =
{
nameof(ObscuredBigInteger),
nameof(ObscuredBool),
nameof(ObscuredDateTime),
nameof(ObscuredDateTimeOffset),
nameof(ObscuredDecimal),
nameof(ObscuredDouble),
nameof(ObscuredFloat),
nameof(ObscuredGuid),
nameof(ObscuredInt),
nameof(ObscuredLong),
nameof(ObscuredQuaternion),
nameof(ObscuredShort),
nameof(ObscuredString),
nameof(ObscuredUInt),
nameof(ObscuredULong),
nameof(ObscuredVector2),
nameof(ObscuredVector2Int),
nameof(ObscuredVector3),
nameof(ObscuredVector3Int),
};
private delegate bool MigrateDelegate(SerializedProperty sp, bool fixOnly);
private static readonly Dictionary<Type, MigrateDelegate> MigrateMappings = new Dictionary<Type, MigrateDelegate>
{
{ typeof(ObscuredBigInteger), Migrate<SerializedObscuredBigInteger> },
{ typeof(ObscuredBool), Migrate<SerializedObscuredBool> },
{ typeof(ObscuredDateTime), Migrate<SerializedObscuredDateTime> },
{ typeof(ObscuredDateTimeOffset), Migrate<SerializedObscuredDateTimeOffset> },
{ typeof(ObscuredDecimal), Migrate<SerializedObscuredDecimal> },
{ typeof(ObscuredDouble), Migrate<SerializedObscuredDouble> },
{ typeof(ObscuredFloat), Migrate<SerializedObscuredFloat> },
{ typeof(ObscuredGuid), Migrate<SerializedObscuredGuid> },
{ typeof(ObscuredInt), Migrate<SerializedObscuredInt> },
{ typeof(ObscuredLong), Migrate<SerializedObscuredLong> },
{ typeof(ObscuredQuaternion), Migrate<SerializedObscuredQuaternion> },
{ typeof(ObscuredShort), Migrate<SerializedObscuredShort> },
{ typeof(ObscuredString), Migrate<SerializedObscuredString> },
{ typeof(ObscuredUInt), Migrate<SerializedObscuredUInt> },
{ typeof(ObscuredULong), Migrate<SerializedObscuredULong> },
{ typeof(ObscuredVector2), Migrate<SerializedObscuredVector2> },
{ typeof(ObscuredVector2Int), Migrate<SerializedObscuredVector2Int> },
{ typeof(ObscuredVector3), Migrate<SerializedObscuredVector3> },
{ typeof(ObscuredVector3Int), Migrate<SerializedObscuredVector3Int> }
};
/// <summary>
/// Checks all assets in project for old version of obscured types and tries to migrate values to the new version
/// or fix corrupt states if possible.
/// </summary>
public static void MigrateProjectAssets(bool skipInteraction = false)
{
MigrateProjectAssets(false, skipInteraction);
}
/// <summary>
/// Checks all assets in project for old version of obscured types and tries to migrate values to the new version
/// or fix corrupt states if possible.
/// </summary>
public static void MigrateProjectAssets(bool fixOnly, bool skipInteraction)
{
MigrateProjectAssets(null, fixOnly, skipInteraction);
}
/// <summary>
/// Checks specified assets in project for old version of obscured types and tries to migrate values to the new version
/// or fix corrupt states if possible.
/// </summary>
public static void MigrateProjectAssets(string[] assetPaths, bool fixOnly, bool skipInteraction)
{
var result = 0;
if (!skipInteraction)
{
if (assetPaths == null || assetPaths.Length == 0)
{
result = EditorUtility.DisplayDialogComplex(ModuleName,
"Are you sure you wish to scan ALL Prefabs and Scriptable Objects and automatically migrate and / or fix invalid values?\n" +
"Select 'Migrate and fix' only if you did update from the older ACTk version.",
"Migrate and fix", "Cancel", "Fix only");
}
else if (assetPaths.Length > 1000)
result = EditorUtility.DisplayDialogComplex(ModuleName,
$"Are you sure you wish to scan {assetPaths.Length} Prefabs and Scriptable Objects and automatically migrate and / or fix invalid values?\n" +
"Select 'Migrate and fix' only if you did update from the older ACTk version.",
"Migrate and fix", "Cancel", "Fix only");
}
if (!skipInteraction)
{
switch (result)
{
case 0:
fixOnly = false;
break;
case 2:
fixOnly = true;
break;
default:
Debug.Log(ACTk.LogPrefix + ModuleName + ": canceled by user.");
return;
}
}
fixOnlyMode = fixOnly;
EditorTools.TraverseSerializedScriptsAssets(assetPaths, ProcessProperty, TypesToMigrate);
Debug.Log(ACTk.LogPrefix + ModuleName + ": complete.");
}
/// <summary>
/// Checks all currently opened scenes for old version of obscured types and tries to migrate values to the new version
/// or fix corrupt states if possible.
/// </summary>
public static void MigrateOpenedScenes(bool skipInteraction = false)
{
MigrateOpenedScenes(false, skipInteraction);
}
/// <summary>
/// Checks all currently opened scenes for old version of obscured types and tries to migrate values to the new version
/// or fix corrupt states if possible.
/// </summary>
public static void MigrateOpenedScenes(bool fixOnly, bool skipInteraction, bool skipSave = false)
{
if (!skipInteraction)
{
var confirmationResult = ConfirmMigration("all opened Scenes");
switch (confirmationResult)
{
case 0:
fixOnly = false;
break;
case 2:
fixOnly = true;
break;
default:
Debug.Log(ACTk.LogPrefix + ModuleName + ": canceled by user.");
return;
}
}
MigrateScenes(EditorTools.TraverseSerializedScriptsInOpenedScenes, fixOnly, skipSave);
}
/// <summary>
/// Checks all scenes in Build Settings for old version of obscured types and tries to migrate values to the new version
/// or fix corrupt states if possible.
/// </summary>
public static void MigrateBuildProfilesScenes(bool skipInteraction = false)
{
MigrateBuildProfilesScenes(false, skipInteraction);
}
/// <summary>
/// Checks all scenes in Build Settings for old version of obscured types and tries to migrate values to the new version
/// or fix corrupt states if possible.
/// </summary>
public static void MigrateBuildProfilesScenes(bool fixOnly, bool skipInteraction, bool skipSave = false)
{
if (!skipInteraction)
{
var confirmationResult = ConfirmMigration("all scenes in " + ACTkMenuItems.BuildProfilesLabel);
switch (confirmationResult)
{
case 0:
fixOnly = false;
break;
case 2:
fixOnly = true;
break;
default:
Debug.Log(ACTk.LogPrefix + ModuleName + ": canceled by user.");
return;
}
}
MigrateScenes(EditorTools.TraverseSerializedScriptsInBuildProfilesScenes, fixOnly, skipSave);
}
private static void MigrateScenes(Action<ProcessSerializedProperty, string[], bool> traverseAction, bool fixOnly, bool skipSave)
{
fixOnlyMode = fixOnly;
var types = TypeCache.GetTypesDerivedFrom<ISerializableObscuredType>().Where(t => !t.IsAbstract && !t.IsInterface)
.Select(type => type.Name).ToArray();
traverseAction(ProcessProperty, types, skipSave);
Debug.Log(ACTk.LogPrefix + ModuleName + ": complete.");
}
private static int ConfirmMigration(string target)
{
return EditorUtility.DisplayDialogComplex(ModuleName,
$"Are you sure you wish to scan {target} and automatically migrate and / or fix invalid values?\n" +
"Select 'Migrate and fix' only if you did update from the older ACTk version.",
"Migrate and fix", "Cancel", "Fix only");
}
private static bool ProcessProperty(Object target, SerializedProperty sp, AssetLocationData location, string type)
{
var obscured = sp.GetValue<ISerializableObscuredType>();
if (obscured == null || obscured.IsDataValid)
return false;
if (MigrateMappings.TryGetValue(obscured.GetType(), out var migrate))
{
var modified = migrate(sp, fixOnlyMode);
if (modified)
Debug.Log($"{ACTk.LogPrefix}{ModuleName} migrated property {sp.displayName}:{type} at\n{location.ToString()}", target);
return modified;
}
return false;
}
private static bool Migrate<TSerialized>(SerializedProperty sp, bool fixOnly) where TSerialized : ISerializedObscuredType, new()
{
var serialized = new TSerialized();
serialized.Init(sp);
if (!fixOnly && serialized.IsCanMigrate)
return serialized.Migrate();
return serialized.Fix();
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 101a5a0542a54c1a854f4459447108d2
timeCreated: 1729782845
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ObscuredTypes/Maintenance/ObscuredTypesMigrator.cs
uploadId: 773557

View File

@ -0,0 +1,141 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using CodeStage.AntiCheat.Common;
using CodeStage.AntiCheat.ObscuredTypes;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace CodeStage.AntiCheat.EditorCode
{
public delegate void InvalidPropertyFound(Object target, SerializedProperty sp, string location, string type);
/// <summary>
/// Class with utility functions to help with ACTk migrations after updates.
/// </summary>
public static class ObscuredTypesValidator
{
internal const string ModuleName = "Obscured Types Validator";
private static InvalidPropertyFound lastPassedCallback;
private static HashSet<string> invalidAssetPaths;
private static int invalidPropertiesFound;
/// <summary>
/// Traverses all prefabs and scriptable objects in the project and checks if they contain any Obscured types with anomalies.
/// </summary>
/// <param name="assetPaths">Specify target asset(s) or pass null to scan whole project.</param>
/// <param name="callback">Pass callback if you wish to process invalid objects.</param>
/// <param name="skipInteraction">Skips intro confirmation.</param>
/// <returns>Array of paths that require migration.</returns>
public static string[] ValidateProjectAssets(string[] assetPaths = null, InvalidPropertyFound callback = null, bool skipInteraction = false)
{
if (!skipInteraction)
{
if (assetPaths == null || assetPaths.Length == 0)
{
if (!EditorUtility.DisplayDialog(ModuleName,
"Are you sure you wish to scan ALL Prefabs and Scriptable Objects?\n" +
"This can take some time to complete.",
"Yes", "No"))
{
Debug.Log(ACTk.LogPrefix + ModuleName + ": canceled by user.");
return null;
}
}
else if (assetPaths.Length > 1000 && !EditorUtility.DisplayDialog(ModuleName,
$"Are you sure you wish to scan {assetPaths.Length} Prefabs and Scriptable Objects?",
"Yes", "No"))
{
Debug.Log(ACTk.LogPrefix + ModuleName + ": canceled by user.");
return null;
}
}
invalidAssetPaths = new HashSet<string>();
var types = TypeCache.GetTypesDerivedFrom<ISerializableObscuredType>().Where(t => !t.IsAbstract && !t.IsInterface)
.Select(type => type.Name).ToArray();
lastPassedCallback = callback;
EditorTools.TraverseSerializedScriptsAssets(assetPaths, ValidateProperty, types);
lastPassedCallback = null;
Debug.Log(ACTk.LogPrefix + ModuleName + ": complete.");
return invalidAssetPaths.ToArray();
}
/// <summary>
/// Traverse all currently opened scenes and checks if they contain any non-valid Obscured types.
/// </summary>
/// <param name="callback">Pass callback if you wish to process invalid objects.</param>
/// <param name="skipInteraction">Skips intro confirmation.</param>
/// <returns>Number of invalid properties.</returns>
public static int ValidateOpenedScenes(InvalidPropertyFound callback = null, bool skipInteraction = false)
{
if (!skipInteraction && !ConfirmValidation("all opened Scenes"))
return invalidPropertiesFound;
return ValidateScenes(EditorTools.TraverseSerializedScriptsInOpenedScenes, callback);
}
/// <summary>
/// Traverses all scenes added to all BuildProfiles (ex BuildSettings) and checks if they contain any non-valid Obscured types.
/// </summary>
/// <param name="callback">Pass callback if you wish to process invalid objects.</param>
/// <param name="skipInteraction">Skips intro confirmation.</param>
/// <returns>Number of invalid properties.</returns>
public static int ValidateBuildProfilesScenes(InvalidPropertyFound callback = null, bool skipInteraction = false)
{
if (!skipInteraction && !ConfirmValidation("all scenes in " + ACTkMenuItems.BuildProfilesLabel))
return invalidPropertiesFound;
return ValidateScenes(EditorTools.TraverseSerializedScriptsInBuildProfilesScenes, callback);
}
private static bool ConfirmValidation(string target)
{
return EditorUtility.DisplayDialog(ModuleName,
$"Are you sure you wish to scan {target} and validate all found obscured types?",
"Yes", "No");
}
private static int ValidateScenes(Action<ProcessSerializedProperty, string[], bool> traverseAction, InvalidPropertyFound callback)
{
invalidPropertiesFound = 0;
var types = TypeCache.GetTypesDerivedFrom<ISerializableObscuredType>().Where(t => !t.IsAbstract && !t.IsInterface)
.Select(type => type.Name).ToArray();
lastPassedCallback = callback;
traverseAction(ValidateProperty, types, true);
lastPassedCallback = null;
Debug.Log(ACTk.LogPrefix + ModuleName + ": complete.");
return invalidPropertiesFound;
}
private static bool ValidateProperty(Object target, SerializedProperty sp, AssetLocationData location, string type)
{
var obscured = sp.GetValue<ISerializableObscuredType>();
if (obscured != null && !obscured.IsDataValid)
{
lastPassedCallback?.Invoke(target, sp, location.ToString(), type);
target = EditorTools.GetPingableObject(target);
Debug.LogWarning($"{ACTk.LogPrefix}{ModuleName} found invalid property [{sp.displayName}] of type [{type}] at:\n{location}", target);
invalidPropertiesFound++;
invalidAssetPaths?.Add(location.AssetPath);
}
return false;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 7ec1712370d34ee1b38697a83d773c14
timeCreated: 1666516545
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ObscuredTypes/Maintenance/ObscuredTypesValidator.cs
uploadId: 773557

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: dfff86e18b5565b499779c37590c4d83
folderAsset: yes
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 73f17e3e136e49bb8ff04e8fe09dc26e
timeCreated: 1724779708

View File

@ -0,0 +1,108 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using CodeStage.AntiCheat.Common;
using CodeStage.AntiCheat.ObscuredTypes;
using CodeStage.AntiCheat.ObscuredTypes.EditorCode;
using UnityEditor;
using UnityEngine;
namespace CodeStage.AntiCheat.EditorCode.PropertyDrawers
{
public abstract class ObscuredTypeDrawer<TSerializedObscuredType, TPlainType> : PropertyDrawer
where TSerializedObscuredType : SerializedObscuredType<TPlainType>, new()
{
protected TSerializedObscuredType serialized;
protected TPlainType plain;
public override void OnGUI(Rect position, SerializedProperty sp, GUIContent label)
{
serialized = new TSerializedObscuredType();
serialized.Init(sp);
plain = serialized.Plain;
var instance = sp.GetValue<ISerializableObscuredType>();
BeforeOnGUI(ref position, ref sp, ref label);
var labelWidth = EditorGUIUtility.labelWidth;
label = EditorGUI.BeginProperty(position, label, sp);
if (instance != null && !instance.IsDataValid)
DrawFixButton(ref position);
EditorGUI.BeginChangeCheck();
DrawProperty(position, sp, label);
if (EditorGUI.EndChangeCheck())
ApplyChanges();
EditorGUI.EndProperty();
EditorGUIUtility.labelWidth = labelWidth;
}
private void DrawFixButton(ref Rect position)
{
var fixPosition = EditorGUI.IndentedRect(position);
var fixButtonRect = new Rect(fixPosition) { width = 40 };
fixPosition.x += 45;
position.x += 45;
position.width -= 45;
EditorGUIUtility.labelWidth -= 45;
DrawFixBackground(fixPosition);
if (GUI.Button(fixButtonRect, new GUIContent("Fix",
"Fix invalid state with either migrating from older version or overriding hash.")))
{
if (serialized.IsCanMigrate)
{
var migrated = serialized.GetMigrationResultString();
EditorApplication.delayCall += () =>
{
var option = EditorUtility.DisplayDialogComplex(
"Anti-Cheat Toolkit",
$"This variable can be migrated to:\n'{migrated}'.\nDo you wish to migrate?",
"Migrate",
"Only fix",
"Cancel"
);
switch (option)
{
case 0:
if (!serialized.Migrate())
ACTk.PrintExceptionForSupport("Couldn't migrate obscured type instance!");
break;
case 1:
if (!serialized.Fix())
ACTk.PrintExceptionForSupport("Couldn't fix obscured type instance!");
break;
case 2:
break;
}
};
}
else
{
if (!serialized.Fix())
ACTk.PrintExceptionForSupport("Couldn't fix obscured type instance!");
}
}
}
protected virtual void DrawFixBackground(Rect position)
{
var width = EditorGUIUtility.labelWidth - EditorGUI.indentLevel * 15;
var bgRect = new Rect(position) { width = width};
EditorGUI.DrawRect(bgRect, new Color32(255, 0, 0, 25));
}
protected abstract void DrawProperty(Rect position, SerializedProperty sp, GUIContent label);
protected abstract void ApplyChanges();
protected virtual void BeforeOnGUI(ref Rect position, ref SerializedProperty sp, ref GUIContent label) { }
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 5a672b282a2a46fe985667865fdba49d
timeCreated: 1724779717
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ObscuredTypes/PropertyDrawers/Abstract/ObscuredTypeDrawer.cs
uploadId: 773557

View File

@ -0,0 +1,27 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using CodeStage.AntiCheat.ObscuredTypes.EditorCode;
using UnityEditor;
using UnityEngine;
namespace CodeStage.AntiCheat.EditorCode.PropertyDrawers
{
public abstract class WideObscuredTypeDrawer<TSerializedObscuredType, TPlainType> : ObscuredTypeDrawer<TSerializedObscuredType, TPlainType>
where TSerializedObscuredType : SerializedObscuredType<TPlainType>, new()
{
protected override void DrawFixBackground(Rect position)
{
if (EditorGUIUtility.wideMode)
base.DrawFixBackground(position);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.wideMode ? EditorGUIUtility.singleLineHeight : EditorGUIUtility.singleLineHeight * 2f;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0d5b5067d6a1403e8c65b6b63f7acade
timeCreated: 1724782025
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ObscuredTypes/PropertyDrawers/Abstract/WideObscuredTypeDrawer.cs
uploadId: 773557

View File

@ -0,0 +1,41 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System.Numerics;
using CodeStage.AntiCheat.ObscuredTypes;
using CodeStage.AntiCheat.ObscuredTypes.EditorCode;
using CodeStage.AntiCheat.Utils;
using UnityEditor;
using UnityEngine;
namespace CodeStage.AntiCheat.EditorCode.PropertyDrawers
{
[CustomPropertyDrawer(typeof(ObscuredBigInteger))]
internal class ObscuredBigIntegerDrawer : ObscuredTypeDrawer<SerializedObscuredBigInteger, BigInteger>
{
private string input;
protected override void DrawProperty(Rect position, SerializedProperty sp, GUIContent label)
{
#if UNITY_2022_1_OR_NEWER
input = EditorGUI.DelayedTextField(position, label, plain.ToString());
#else
input = EditorGUI.TextField(position, label, plain.ToString());
#endif
}
protected override void ApplyChanges()
{
if (!BigInteger.TryParse(input, out var newValue))
newValue = 0;
plain = newValue;
serialized.Hidden = ObscuredBigInteger.Encrypt(plain, serialized.Key);
serialized.Hash = HashUtils.CalculateHash(plain);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c48a363379404a3f8ffa0970bd1f7653
timeCreated: 1653693062
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ObscuredTypes/PropertyDrawers/ObscuredBigIntegerDrawer.cs
uploadId: 773557

View File

@ -0,0 +1,29 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using CodeStage.AntiCheat.ObscuredTypes;
using CodeStage.AntiCheat.ObscuredTypes.EditorCode;
using CodeStage.AntiCheat.Utils;
using UnityEditor;
using UnityEngine;
namespace CodeStage.AntiCheat.EditorCode.PropertyDrawers
{
[CustomPropertyDrawer(typeof(ObscuredBool))]
internal class ObscuredBoolDrawer : ObscuredTypeDrawer<SerializedObscuredBool, bool>
{
protected override void DrawProperty(Rect position, SerializedProperty sp, GUIContent label)
{
plain = EditorGUI.Toggle(position, label, plain);
}
protected override void ApplyChanges()
{
serialized.Hidden = ObscuredBool.Encrypt(plain, serialized.Key);
serialized.Hash = HashUtils.CalculateHash(plain);
}
}
}

View File

@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: 2a0ae33a4804dd5468c37cee3d9e4a15
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ObscuredTypes/PropertyDrawers/ObscuredBoolDrawer.cs
uploadId: 773557

View File

@ -0,0 +1,57 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System;
using System.Globalization;
using CodeStage.AntiCheat.ObscuredTypes;
using CodeStage.AntiCheat.ObscuredTypes.EditorCode;
using CodeStage.AntiCheat.Utils;
using UnityEditor;
using UnityEngine;
namespace CodeStage.AntiCheat.EditorCode.PropertyDrawers
{
[CustomPropertyDrawer(typeof(ObscuredDateTime))]
internal class ObscuredDateTimeDrawer : ObscuredTypeDrawer<SerializedObscuredDateTime, DateTime>
{
private string input;
protected override void DrawProperty(Rect position, SerializedProperty sp, GUIContent label)
{
var posW = position.width;
position.width *= 0.75f;
var kindRect = position;
kindRect.x = position.xMax + 5;
kindRect.width = posW - position.width - 5;
var dateString = plain.ToString("o", DateTimeFormatInfo.InvariantInfo);
input = EditorGUI.DelayedTextField(position, label, dateString);
label = EditorGUI.BeginProperty(kindRect, GUIContent.none, sp);
using (var scope = new EditorGUI.ChangeCheckScope())
{
var kind = plain.Kind;
var kindInput = (DateTimeKind)EditorGUI.EnumPopup(kindRect, label, kind);
if (scope.changed)
{
plain = DateTime.SpecifyKind(plain, kindInput);
input = plain.ToString("o", DateTimeFormatInfo.InvariantInfo);
ApplyChanges();
}
}
EditorGUI.EndProperty();
}
protected override void ApplyChanges()
{
DateTime.TryParse(input, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out plain);
serialized.Hidden = ObscuredDateTime.Encrypt(plain, serialized.Key);
serialized.Hash = HashUtils.CalculateHash(plain);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d4822eb673254d3fa09b8add898e8445
timeCreated: 1684493697
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ObscuredTypes/PropertyDrawers/ObscuredDateTimeDrawer.cs
uploadId: 773557

View File

@ -0,0 +1,36 @@
#region copyright
// ------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov [https://codestage.net]
// ------------------------------------------------------
#endregion
using System;
using System.Globalization;
using CodeStage.AntiCheat.ObscuredTypes;
using CodeStage.AntiCheat.ObscuredTypes.EditorCode;
using CodeStage.AntiCheat.Utils;
using UnityEditor;
using UnityEngine;
namespace CodeStage.AntiCheat.EditorCode.PropertyDrawers
{
[CustomPropertyDrawer(typeof(ObscuredDateTimeOffset))]
internal class ObscuredDateTimeOffsetDrawer : ObscuredTypeDrawer<SerializedObscuredDateTimeOffset, DateTimeOffset>
{
private string input;
protected override void DrawProperty(Rect position, SerializedProperty sp, GUIContent label)
{
var dateString = plain.ToString("o", DateTimeFormatInfo.InvariantInfo);
input = EditorGUI.DelayedTextField(position, label, dateString);
}
protected override void ApplyChanges()
{
DateTimeOffset.TryParse(input, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out plain);
serialized.Hidden = ObscuredDateTimeOffset.Encrypt(plain, serialized.Key);
serialized.Hash = HashUtils.CalculateHash(plain.UtcTicks);
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 6c0b323727b533b45af6ac5ade85b55d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 202695
packageName: Anti-Cheat Toolkit
packageVersion: 6.2.0
assetPath: Assets/Plugins/CodeStage/AntiCheatToolkit/Editor/Scripts/ObscuredTypes/PropertyDrawers/ObscuredDateTimeOffsetDrawer.cs
uploadId: 773557

Some files were not shown because too many files have changed in this diff Show More