Unity find and delete all files named "Delete This File.txt" - c#

I have a unity package with my folder structure pre-setup, but meant to be empty, in order to export the folders I put a file "Delete This File.txt" into every bottom folder in the structure to allow unity to export the folders, but I want to write a script that runs on startup which deletes these files, but i can't seem to get the script to find all the files throughout the various folders. How do i do this?
using UnityEngine;
using UnityEditor;
using System.IO;
[InitializeOnLoad]
public class Startup {
static Startup()
{
foreach (string file in Directory.GetFiles("Assets/")) {
if (file == "Delete This File.txt") {
Debug.Log (file);
} else {
// Do Nothing
}
}
}
}

Some changes are required in your code
[InitializeOnLoad]
public class Startup {
static Startup()
{
// unfortunately this is not available in NET 3.5
// foreach (string file in Directory.EnumerateFiles.....
foreach (string file in Directory.GetFiles("Assets", "Delete This File.txt", SearchOption.AllDirectories))
{
Debug.Log (file);
// File.Delete(file);
}
}
This approach uses the overload of Directory.GetFiles that takes the initial directory, the pattern to search for and the option to search all subdirectory of the root. The resulting strings are the full filenames to delete.
Also, in NET 4.0, there is a better option with Directory.EnumerateFiles as explained by the MSDN docs
The EnumerateFiles and GetFiles methods differ as follows: When you
use EnumerateFiles, you can start enumerating the collection of names
before the whole collection is returned; when you use GetFiles, you
must wait for the whole array of names to be returned before you can
access the array. Therefore, when you are working with many files and
directories, EnumerateFiles can be more efficient.

This can be done with the combination of Directory.GetFiles,Directory.GetDirectories and FileInfo classes. I modified Microsoft's RecursiveFileProcessor code example and added FileInfo and an if statement to make it find all files named "Delete This File.txt" and delete them.
using UnityEngine;
using UnityEditor;
using System.IO;
[InitializeOnLoad]
public class Startup
{
static Startup()
{
ProcessDirectory("Assets/");
}
public static void ProcessDirectory(string targetDirectory)
{
// Process the list of files found in the directory.
string[] fileEntries = Directory.GetFiles(targetDirectory);
foreach (string filePath in fileEntries)
{
FileInfo fileInfo = new FileInfo(filePath);
if (fileInfo.Name == "Delete This File.txt")
{
ProcessFile(filePath);
}
}
// Recurse into subdirectories of this directory.
string[] subdirectoryEntries = Directory.GetDirectories(targetDirectory);
foreach (string subdirectory in subdirectoryEntries)
{
ProcessDirectory(subdirectory);
}
}
// Insert logic for processing found files here.
public static void ProcessFile(string path)
{
Debug.Log("Processed file " + path);
//Delete File
File.Delete(path);
}
}

After discovering that Directory.EnumerateFiles wasn't an option for me I tried the same method with the get files, and found a solution that works. This code not only deletes the text file but the meta file that is associated with it, counting the number of times a text file was deleted and informing me how many were deleted in the log.
using UnityEngine;
using UnityEditor;
using System.IO;
/* Project Name : Ember Guard
* Script Name : RemoveDeleteFile
* Script Path : Assets/Editor/RemoveDeleteFile.cs
* Script Author : FaalFaazDov || Raisltin M. Thoreson
* Created On : 29/08/2016 14:24
* Modified On : N/A
* Version : 0.0.1Alpha
*/
/*************************************************************************
*
* Biophase Entertainment
* __________________
*
* [2016] Biophase Entertainment
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Biophase Entertainment and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Biophase Entertainment
* and its suppliers and may be covered by Canadian and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information, reproduction of this material or attempting
* to read the content of these files is strictly forbidden unless prior written
* permission is obtained from Biophase Entertainment.
*
*/
[InitializeOnLoad]
public class Startup {
static Startup()
{
int x = 0;
string path = Application.dataPath;
string[] files = Directory.GetFiles (path, "Delete This File.txt", SearchOption.AllDirectories);
foreach (string f in files) {
File.Delete (f);
File.Delete (f + ".meta");
x++;
}
string fm;
if (x == 1) {
fm = "file";
} else {
fm = "files";
}
Debug.Log ("Deleted " + x + " " + fm + ".");
}
}

Related

UnRAR DLL unable to be referenced in C#

I was trying to make a program to clean out some directories on my NAS and I noticed that a lot of folders contained nested rar and zip files and I have plenty of space to unpack them. The program should ask the user for a directory to be cleaned then unpack all rars then delete all of the rars. I'm trying to use UnRAR DLL and I cant even get the rars to unpack. I realize I'm having an issue where visual studio 2022 is refusing to recognize the Unrar DLL in the "using" command. Because of that I've been unable to unpack a single file. This is one my first useful programs so if im missing something basic I understand.
This is my initial attempt:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
using UnRAR;
namespace Cleaning
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter Directory To Be Cleaned");
string rar_path = Console.ReadLine();
string[] Rars = Directory.GetFiles(rar_path, "*.rar", SearchOption.AllDirectories);
foreach (string rar in Rars)
{
string source = rar;
string dest = "C:\\Users\\Kaleb\\OneDrive\\Desktop\Test Area";
UnRAR unrar = new UnRAR();
unrar.Password = "password_of_myarchive";
unrar.Open(#source, UnRAR.OpenMode.Extract);
while (unrar.ReadHeader())
{
unrar.ExtractToDirectory(#dest);
}
unrar.Close();
}
}
}
}
For reference I have added the UnRAR DLL to the project folder.
SO I was able to get it working with the source code from the great people over at SharpCompress and utilizing their source I've got the following stable build.
using SharpCompress.Archives;
using SharpCompress.Archives.Rar;
using SharpCompress.Common;
using System;
using System.IO;
using System.Linq;
using System.Globalization;
namespace ConsoleApp3
{
public class Program
{
static void Main(string[] args)
{
for (; ; )
{
Console.WriteLine("Enter E to extract all directories in file path");
Console.WriteLine("Enter D to delete all Archives in file path");
Console.WriteLine("REMEMBER TO ALWAYS EXTRACT BEFORE DELETING");
string option = Console.ReadLine();
if (option == "e" || option == "E")
{
Console.WriteLine("Enter Directory To Be Cleaned");
//as a warning this will extract all files from any rar in the slected driectory one at a time in order.
//if a rar is broken it will halt the program until the offendin rar is deleted best way to find is to see what has been extracted so far and go from there
//or one could also limit the directory in order to refine the number of rars to look for
string rar_path = Console.ReadLine();
string[] Rars = Directory.GetFiles(rar_path, "*.rar", SearchOption.AllDirectories);
foreach (string rar in Rars)
{
var DirectoryFinal = Path.GetDirectoryName(rar);
using (var archive = RarArchive.Open(#rar))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
entry.WriteToDirectory(#DirectoryFinal, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true
});
}
};
}
}
else if (option == "d" || option == "D")
{
Console.WriteLine("Enter Directory To Be Cleaned");
//be careful with this i would recomend extracting and then chekcing everything first
string rar_path = Console.ReadLine();
string[] TobeDeleted = Directory.GetFiles(rar_path, "*.r*", SearchOption.AllDirectories);
foreach (string rarstobedeleted in TobeDeleted)
{
File.Delete(rarstobedeleted);
}
}
else
{
Console.WriteLine("Thats not an option try again");
}
Console.WriteLine("Cleaning Complete.");
;
}
}
}
}
This work effectively for rar files only for the time being but will effectively clean up any directories where someone may have downloaded a large amount of files stored in separated rars

How do I loop over all Assets folders and sub folders and get to a list all the prefabs in this folders?

This code get all the sub folders in a Assets specific folder.
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
public class AddComponents : Editor
{
private static string GetClickedDirFullPath()
{
string clickedAssetGuid = Selection.assetGUIDs[0];
string clickedPath = AssetDatabase.GUIDToAssetPath(clickedAssetGuid);
string clickedPathFull = Path.Combine(Directory.GetCurrentDirectory(), clickedPath);
FileAttributes attr = File.GetAttributes(clickedPathFull);
return attr.HasFlag(FileAttributes.Directory) ? clickedPathFull : Path.GetDirectoryName(clickedPathFull);
}
private static string GetPath()
{
string path = GetClickedDirFullPath();
int index = path.IndexOf("Assets");
string result = path.Substring(index);
return result;
}
private static string[] GetSubFoldersRecursive(string root)
{
var paths = new List<string>();
// If there are no further subfolders then AssetDatabase.GetSubFolders returns
// an empty array => foreach will not be executed
// This is the exit point for the recursion
foreach (var path in AssetDatabase.GetSubFolders(root))
{
// add this subfolder itself
paths.Add(path);
// If this has no further subfolders then simply no new elements are added
paths.AddRange(GetSubFoldersRecursive(path));
}
return paths.ToArray();
}
[MenuItem("Assets/Get Folders")]
private static void GetFolders()
{
List<string> paths = new List<string>();
string selectedPath = GetPath();
var folders = GetSubFoldersRecursive(selectedPath);
foreach (var path in folders)
{
paths.Add(path);
}
}
}
And this get all the prefabs from a specific folder but when I type the folder name :
public List<string> prefabsPaths;
public void AddComponentsToObjects()
{
prefabsPaths = new List<string>();
string[] assetsPaths = AssetDatabase.GetAllAssetPaths();
foreach (string assetPath in assetsPaths)
{
if (assetPath.Contains("Archanor"))
{
if (assetPath.Contains("Prefabs"))
{
prefabsPaths.Add(assetPath);
}
}
}
}
I want to combine both codes so when I make right click and select Get Path it will get all the prefabs from the selected path the right click on the path and the path all sub folders recursive.
Example :
Assets
Folder1
Folder2
Folder3
Folder4
Folder5
Folder6
If in the Assets I make mouse right click on the folder Folder2 and then select from the menu Get Path I want it to get all the prefabs from Folder2 and all the sub folders under Folder2. So In the end I will have a List of all the paths with the folders :
Folder2\1.prefab
Folder2\Folder3\2.prefab
Folder2\Folder3\3.prefab
Folder2\Folder4\4.prefab
In the end my goal is to be able to add to each prefab a component for example a Rigidbody.
So if I have a folder with the 10 prefabs and under it 30 sub folders with more 200 prefabs then get all the folders and prefabs and add a component/s to all the prefabs in all folders.
Here is an alternative and simple way to do this.
string[] files = Directory.GetFiles("DirectoryPath", "*.prefab", SearchOption.AllDirectories);
You can use "Resources.Load("[path]/[filename]") as [Class]" this will give you the asset, prefab, sprite etc you need. Important to note that this will only look in the folder "Resources" in the asset folder (the root in the unity project), so you have to create a "Resources" folder! This is also just a good idea because it can be very slow to look trough all files so this ensures it only looks at the important files. So you can use Vivek nuna's solution to get the file locations and Resources.Load to get the asset. For more information look here: https://docs.unity3d.com/ScriptReference/Resources.html
Hope this helps!
To get actual prefab from path, use AssetDatabase.LoadAssetAtPath(path) and related methods.
It only works in editor, but I guess thats what you need
This is what I wanted and working good for my case :
In the Assets right click on any Folder and then click on Add Components.
It will loop all the folders and sub folders of the folder you did right click Add Components. Then it will find all the prefabs add to them a Rigidbody and save the changes.
Depending on how many prefabs you have it might be a bit slowly but it's doing the job.
Things to add :
Options to add more components or multiple components.
Creating a log/text file with all the changes made prefabs names what changes made and when.
Undo if there an option to make undo on Assets or to make the undo using the text file reading the changes made and revert back.
Backup : Once adding components to make a backup first in a temp folder of the original prefabs (could be also use for undo cases ).
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using UnityEditor;
using UnityEngine;
public class AddComponents : Editor
{
private static string GetClickedDirFullPath()
{
string clickedAssetGuid = Selection.assetGUIDs[0];
string clickedPath = AssetDatabase.GUIDToAssetPath(clickedAssetGuid);
string clickedPathFull = Path.Combine(Directory.GetCurrentDirectory(), clickedPath);
FileAttributes attr = File.GetAttributes(clickedPathFull);
return attr.HasFlag(FileAttributes.Directory) ? clickedPathFull : Path.GetDirectoryName(clickedPathFull);
}
private static string GetPath()
{
string path = GetClickedDirFullPath();
int index = path.IndexOf("Assets");
string result = path.Substring(index);
return result;
}
static List<string> paths = new List<string>();
private static void GetFolders()
{
string selectedPath = GetPath();
string[] assetsPaths = AssetDatabase.GetAllAssetPaths();
foreach (string assetPath in assetsPaths)
{
if (assetPath.Contains(selectedPath))
{
if (assetPath.Contains("Prefabs"))
{
paths.Add(assetPath);
}
}
}
}
[MenuItem("Assets/Add Components")]
public static void AddComponents()
{
GetFolders();
for (int i = 0; i < paths.Count; i++)
{
if (File.Exists(paths[i]))
{
GameObject contentsRoot = PrefabUtility.LoadPrefabContents(paths[i]);
// Modify Prefab contents.
contentsRoot.AddComponent<Rigidbody>();
// Save contents back to Prefab Asset and unload contents.
PrefabUtility.SaveAsPrefabAsset(contentsRoot, paths[i]);
PrefabUtility.UnloadPrefabContents(contentsRoot);
}
}
}
}

Use monobehaviour from asmdef generated assembly

We would like to distribute our project with assembly files instead of .cs scripts.
We thought that this would be easy thanks to assembly definition files, as unity is creating assembly files for the scripts they refer to anyway.
It turns out that when removing the .cs files and putting the assemblies, we ran into a problem :
The monobehaviors defined in the assemblies (so previously in our scripts) can't be added manually to a scene :
"Can't add script component xxx because the script class cannot be found"
While if we add the component through script (i.e. AddComponent) it works.
I'm using Unity 2017.3.f1 to generate the assembly files
Is there a trick to make this work ? or should I try to generate the assemblies using another approach ?
OP here.
Short answer is : don't keep both asmdef and assembly files. Remove the asmdef file if you replace the scripts with the generated assembly
What I ended up doing is the roughly following (this was for CI purpose):
First, we need to make sure Unity compiles the assembly file. So I have a GenerateAssemblies.cs file in an Editor folder that can be executed from command line:
GenerateAssemblies.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
[InitializeOnLoad]
public static class GenerateAssemblies
{
private static string BATCH_MODE_PARAM = "-batchmode";
private const string REPLACE_ASSEMBLY_PARAM = "-replaceassembly";
static GenerateAssemblies()
{
List<String> args = Environment.GetCommandLineArgs().ToList();
if (args.Any(arg => arg.ToLower().Equals(BATCH_MODE_PARAM)))
{
Debug.LogFormat("GenerateAssemblies will try to parse the command line to replace assemblies.\n" +
"\t Use {0} \"assemblyname\" for every assembly you wish to replace"
, REPLACE_ASSEMBLY_PARAM);
}
if (args.Any(arg => arg.ToLower().Equals(REPLACE_ASSEMBLY_PARAM))) // is a replacement requested ?
{
int lastIndex = 0;
while (lastIndex != -1)
{
lastIndex = args.FindIndex(lastIndex, arg => arg.ToLower().Equals(REPLACE_ASSEMBLY_PARAM));
if (lastIndex >= 0 && lastIndex + 1 < args.Count)
{
string assemblyToReplace = args[lastIndex + 1];
if (!assemblyToReplace.EndsWith(ReplaceAssemblies.ASSEMBLY_EXTENSION))
assemblyToReplace = assemblyToReplace + ReplaceAssemblies.ASSEMBLY_EXTENSION;
ReplaceAssemblies.instance.AddAssemblyFileToReplace(assemblyToReplace);
Debug.LogFormat("Added assembly {0} to the list of assemblies to replace.", assemblyToReplace);
lastIndex++;
}
}
CompilationPipeline.assemblyCompilationFinished += ReplaceAssemblies.instance.ReplaceAssembly; /* This serves as callback after Unity as compiled an assembly */
Debug.Log("Forcing recompilation of all scripts");
// to force recompilation
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone) + ";DUMMY_SYMBOL");
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
}
}
}
Then I have a ReplaceAssemblies.cs file in an editor folder that will :
find the assembly file correpsonding to the asmdef file
save the guid/classes correspondance of the script files
move the script files in a temporary folder
move the assembly in the same folder as the asmdef file
move the asmdef to a temporary folder
Replace the Guid and File ID values for each script in the assembly (to avoid breaking references in scenes and prefabs)
ReplaceAssemblies.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
public class ReplaceAssemblies : ScriptableSingleton<ReplaceAssemblies>
{
public static string ASSEMBLY_EXTENSION = ".dll";
public static string ASSEMBLY_DEFINITION_EXTENSION = ".asmdef";
[SerializeField]
private List<String> assembliesFilesToReplace = new List<string>();
[SerializeField]
private List<string> pathsOfAssemblyFilesInAssetFolder = new List<string>();
[SerializeField]
private List<string> pathsOfAssemblyFilesCreatedByUnity = new List<string>();
[SerializeField]
private string tempSourceFilePath;
private static readonly string[] fileListPath = { "*.prefab", "*.unity", "*.asset" };
public string TempSourceFilePath
{
get
{
if (String.IsNullOrEmpty(tempSourceFilePath))
{
tempSourceFilePath = FileUtil.GetUniqueTempPathInProject();
}
return tempSourceFilePath;
}
}
void OnEnable()
{
Debug.Log("temp dir : " + TempSourceFilePath);
}
public void ReplaceAssembly(string assemblyPath, CompilerMessage[] messages)
{
string assemblyFileName = assembliesFilesToReplace.Find(assembly => assemblyPath.EndsWith(assembly));
// is this one of the assemblies we want to replace ?
if (!String.IsNullOrEmpty(assemblyFileName))
{
string[] assemblyDefinitionFilePaths = Directory.GetFiles(".", Path.GetFileNameWithoutExtension(assemblyFileName) + ASSEMBLY_DEFINITION_EXTENSION, SearchOption.AllDirectories);
if (assemblyDefinitionFilePaths.Length > 0)
{
string assemblyDefinitionFilePath = assemblyDefinitionFilePaths[0];
ReplaceAssembly(assemblyDefinitionFilePath);
}
}
}
public void AddAssemblyFileToReplace(string assemblyFile)
{
assembliesFilesToReplace.Add(assemblyFile);
}
private void ReplaceAssembly(string assemblyDefinitionFilePath)
{
Debug.LogFormat("Replacing scripts for assembly definition file {0}", assemblyDefinitionFilePath);
string asmdefDirectory = Path.GetDirectoryName(assemblyDefinitionFilePath);
string assemblyName = Path.GetFileNameWithoutExtension(assemblyDefinitionFilePath);
Assembly assemblyToReplace = CompilationPipeline.GetAssemblies().ToList().Find(assembly => assembly.name.ToLower().Equals(assemblyName.ToLower()));
string assemblyPath = assemblyToReplace.outputPath;
string assemblyFileName = Path.GetFileName(assemblyPath);
string[] assemblyFilePathInAssets = Directory.GetFiles("./Assets", assemblyFileName, SearchOption.AllDirectories);
// save the guid/classname correspondance of the scripts that we will remove
Dictionary<string, string> oldGUIDToClassNameMap = new Dictionary<string, string>();
if (assemblyFilePathInAssets.Length <= 0)
{
// Move all script files outside the asset folder
foreach (string sourceFile in assemblyToReplace.sourceFiles)
{
string tempScriptPath = Path.Combine(TempSourceFilePath, sourceFile);
Directory.CreateDirectory(Path.GetDirectoryName(tempScriptPath));
if (!File.Exists(sourceFile))
Debug.LogErrorFormat("File {0} does not exist while the assembly {1} references it.", sourceFile, assemblyToReplace.name);
Debug.Log("will move " + sourceFile + " to " + tempScriptPath);
// save the guid of the file because we may need to replace it later
MonoScript monoScript = AssetDatabase.LoadAssetAtPath<MonoScript>(sourceFile);
if (monoScript != null && monoScript.GetClass() != null)
oldGUIDToClassNameMap.Add(AssetDatabase.AssetPathToGUID(sourceFile), monoScript.GetClass().FullName);
FileUtil.MoveFileOrDirectory(sourceFile, tempScriptPath);
}
Debug.Log("Map of GUID/Class : \n" + String.Join("\n", oldGUIDToClassNameMap.Select(pair => pair.Key + " : " + pair.Value).ToArray()));
string finalAssemblyPath = Path.Combine(asmdefDirectory, assemblyFileName);
Debug.Log("will move " + assemblyPath + " to " + finalAssemblyPath);
FileUtil.MoveFileOrDirectory(assemblyPath, finalAssemblyPath);
string tempAsmdefPath = Path.Combine(TempSourceFilePath, Path.GetFileName(assemblyDefinitionFilePath));
Debug.Log("will move " + assemblyDefinitionFilePath + " to " + tempAsmdefPath);
FileUtil.MoveFileOrDirectory(assemblyDefinitionFilePath, tempAsmdefPath);
// Rename the asmdef meta file to the dll meta file so that the dll guid stays the same
FileUtil.MoveFileOrDirectory(assemblyDefinitionFilePath + ".meta", finalAssemblyPath + ".meta");
pathsOfAssemblyFilesInAssetFolder.Add(finalAssemblyPath);
pathsOfAssemblyFilesCreatedByUnity.Add(assemblyPath);
// We need to refresh before accessing the assets in the new assembly
AssetDatabase.Refresh();
// We need to remove .\ when using LoadAsslAssetsAtPath
string cleanFinalAssemblyPath = finalAssemblyPath.Replace(".\\", "");
var assetsInAssembly = AssetDatabase.LoadAllAssetsAtPath(cleanFinalAssemblyPath);
// list all components in the assembly file.
var assemblyObjects = assetsInAssembly.OfType<MonoScript>().ToArray();
// save the new GUID and file ID for the MonoScript in the new assembly
Dictionary<string, KeyValuePair<string, long>> newMonoScriptToIDsMap = new Dictionary<string, KeyValuePair<string, long>>();
// for each component, replace the guid and fileID file
for (var i = 0; i < assemblyObjects.Length; i++)
{
long dllFileId;
string dllGuid = null;
if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(assemblyObjects[i], out dllGuid, out dllFileId))
{
string fullClassName = assemblyObjects[i].GetClass().FullName;
newMonoScriptToIDsMap.Add(fullClassName, new KeyValuePair<string, long>(dllGuid, dllFileId));
}
}
Debug.Log("Map of Class/GUID:FILEID : \n" + String.Join("\n", newMonoScriptToIDsMap.Select(pair => pair.Key + " : " + pair.Value.Key + " - " + pair.Value.Value).ToArray()));
ReplaceIdsInAssets(oldGUIDToClassNameMap, newMonoScriptToIDsMap);
}
else
{
Debug.Log("Already found an assembly file named " + assemblyFileName + " in asset folder");
}
}
/// <summary>
/// Replace ids in all asset files using the given maps
/// </summary>
/// <param name="oldGUIDToClassNameMap">Maps GUID to be replaced => FullClassName</param>
/// <param name="newMonoScriptToIDsMap">Maps FullClassName => new GUID, new FileID</param>
private static void ReplaceIdsInAssets(Dictionary<string, string> oldGUIDToClassNameMap, Dictionary<string, KeyValuePair<string, long>> newMonoScriptToIDsMap)
{
StringBuilder output = new StringBuilder("Report of replaced ids : \n");
// list all the potential files that might need guid and fileID update
List<string> fileList = new List<string>();
foreach (string extension in fileListPath)
{
fileList.AddRange(Directory.GetFiles(Application.dataPath, extension, SearchOption.AllDirectories));
}
foreach (string file in fileList)
{
string[] fileLines = File.ReadAllLines(file);
for (int line = 0; line < fileLines.Length; line++)
{
//find all instances of the string "guid: " and grab the next 32 characters as the old GUID
if (fileLines[line].Contains("guid: "))
{
int index = fileLines[line].IndexOf("guid: ") + 6;
string oldGUID = fileLines[line].Substring(index, 32); // GUID has 32 characters.
if (oldGUIDToClassNameMap.ContainsKey(oldGUID) && newMonoScriptToIDsMap.ContainsKey(oldGUIDToClassNameMap[oldGUID]))
{
fileLines[line] = fileLines[line].Replace(oldGUID, newMonoScriptToIDsMap[oldGUIDToClassNameMap[oldGUID]].Key);
output.AppendFormat("File {0} : Found GUID {1} of class {2}. Replaced with new GUID {3}.", file, oldGUID, oldGUIDToClassNameMap[oldGUID], newMonoScriptToIDsMap[oldGUIDToClassNameMap[oldGUID]].Key);
if (fileLines[line].Contains("fileID: "))
{
index = fileLines[line].IndexOf("fileID: ") + 8;
int index2 = fileLines[line].IndexOf(",", index);
string oldFileID = fileLines[line].Substring(index, index2 - index); // GUID has 32 characters.
fileLines[line] = fileLines[line].Replace(oldFileID, newMonoScriptToIDsMap[oldGUIDToClassNameMap[oldGUID]].Value.ToString());
output.AppendFormat("Replaced fileID {0} with {1}", oldGUID, newMonoScriptToIDsMap[oldGUIDToClassNameMap[oldGUID]].Value.ToString());
}
output.Append("\n");
}
}
}
//Write the lines back to the file
File.WriteAllLines(file, fileLines);
}
Debug.Log(output.ToString());
}
[MenuItem("Tools/Replace Assembly")]
public static void ReplaceAssemblyMenu()
{
string assemblyDefinitionFilePath = EditorUtility.OpenFilePanel(
title: "Select Assembly Definition File",
directory: Application.dataPath,
extension: ASSEMBLY_DEFINITION_EXTENSION.Substring(1));
if (assemblyDefinitionFilePath.Length == 0)
return;
instance.ReplaceAssembly(assemblyDefinitionFilePath);
}
}
I was experiencing this issue, and like you, I was using the information provided from asmdef files to provide all the required information (which .cs files, what references, defines, etc) to build an assembly.
I found that the issue was the DLL I was creating had the same name as the asmdef file I was using to provide the information. Even though the asmdef file was no longer being compiled (because all the scripts had been removed to build the DLL), it was still interfering with the project.
So for me, the inconsistency between accessing a script from inside the editor and from inside scripts was because there was a DLL and as asmdef file with the same name in the project.
Giving the compiled DLL a different name or removing the asmdef file was the solution for me.
Just tested with Unity 2019.3.0b1.
Content of test class:
using System.Reflection;
using UnityEngine;
namespace Assets.Test
{
public class TestBehaviour : MonoBehaviour
{
void Start()
{
Debug.Log(Assembly.GetAssembly(GetType()));
}
}
}
First project with source code and assembly definition file
Second project with the generated DLL, working as expected
As far as i'm concerned, the use of asmdef merely forces unity3d to compile your scripts into separate assemblies that are then referenced by your project.
Actually, it creates projects in your unity solution that contain your .cs files and each of these projects is compiled into its own output assembly.
The error you are seeing might be related to assembly caching.
I've had that error a few months ago and it was due to an outdated assembly still being cached.
As a result, unity3d editor kinda hiccuped when loading the project and therefore could not load the specific assembly.
I fixed it by deleting the directories Library, obj and Temp and then reloaded the unity3d project.
To get rid of that for good, we have moved away from asmdef and .cs files inside our unity projects once and for all.
All our scripts have been extracted to separate projects that are never touched by unity3d.
Every project references UnityEngine.dll and/or UnityEditor.dll (for Editor assemblies) depending on which unity3d types it may require.
The projects are built locally using Visual Studio or server side in our CI pipeline.
Output is copied manually into the assets directory of a unity project where it is then loaded from within unity3d editor.
This last step is a pain admittedly but i have yet to find time to streamline this process some more.
This approach has a few benefits
We are in control of our code in one single repository.
There is only one single point of truth and every developer commits changes onto the same code base.
There are no copies of .cs files across any number of unity projects that consume our types.
There is no need to figure out merge conflicts from updating a unitypackage where there have been deletions.
Unit tests can be done server side (CI pipeline) without the need of some docker image with unity3d on top (ofc there are restrictions on how much you can test without the entire unity3d environment running).
We create our own NuGet packages that can be referenced in projects (vcproj, not unity projects!).
Types deriving from MonoBehaviour can be added to GameObjects via code or via unity3d editor.
You also get to explore loaded assemblies inside your unity3d editor project view by clicking on the arrow of an assembly which will expand to show the list of contained relevant types.
Let's talk about downsides
One example is that we use SteamVR for interacting with controls.
The SteamVR plugin for unity3d is released through unity's asset store and annoyingly it contains script files and resources but no assemblies.
This goes for pretty much all assets in the store by the way.
Since we can't build against code, we have to go through the trouble of compiling SteamVR once and then copy the output assembly somewhere else.
This is not just as tedious as a task can be, it also has some limitations of its own which i get to later.
Anyway, this lets us reference a compiled asset with our own code so we get to use asset specific types like SteamVR_Action in our code without having to use unity3d editor and script files in unity projects (or reflection which would be even worse).
Limitations of compiled assets like this are two fold.
For once, it is horribly inefficient to get there in the first place.
On the other hand, you'll only have to do that once for every version of an asset.
Once that's done, make it a private NuGet package and you're golden.
The other limitation is the way how unity3d approaches dependency injection.
Actually i'm not entirely sure what it really is they try to do but here goes.
Unity3d wants you to only ever reference assemblies from within ../UnityInstallDirectory/Editor/Data/Managed/.
In a perfect world, your own assemblies reference that big gunky UnityEngine.dll in this directory and once loaded by unity3d editor everything works as expected.
When you compile a unity project from within unity3d editor however, the resulting assembly references all the assemblies from within ../UnityInstallDirectory/Editor/Data/Managed/UnityEngine/ which contains a very small version of UnityEngine.dll which in turn acts as a type forwarder to all the other sub modules.
Not such a perfect world now is it?
Your previously compiled asset requires the type MonoBehaviour to sit in an assembly called UnityEngine.CoreModule.dll.
Your own project however expects it to sit in UnityEngine.dll since you're a good fellow and follow the rules.
This is just asking for trouble and to get around this problem we are now directly referencing all the managed sub modules from within ../UnityInstallDirectory/Editor/Data/Managed/UnityEngine/.
We also ignore unity3d editor moaning about how we are doing it wrong.
tl;dr
By doing all from above and leaving asmdef and .cs files out of the equation we are able to build, unit test and pack our logic and types into assemblies.
We are also able to keep a clean code base that can be easily maintained and extended without dozens of copies of the same code in multiple locations and/or repositories.
Why unity3d does things the way they do, i'll never understand.
I do know there is a thing called Building from HEAD but since the entirety of the .net ecosystem is using the binary format to share content in the form of referable assemblies, why would you want to do things differently?
This is a topic for another day though.
If you made it all the way through this post, i sincerely hope it is helping you fix your problem at hand.
In case i misinterpreted your question ... sorry :-)
Unity3d is weird ...

Directory Check Won't Work

I tried creating a program that tells you if a directory exists or not, but no matter what I input, it always comes up as not existing.
My Code:
using System;
using System.IO;
class TestFileAndDirectory
{
public static void Main()
{
string input;
input = Console.ReadLine();
if ( Directory.Exists(input))
{
Console.WriteLine("Exists");
}
else
{
Console.WriteLine("Doesn't Exist");
}
Console.ReadLine();
}
}
At first I just thought maybe it was my logic, so I tried this code from the book: Microsoft Visual C# 2010: Comprehensive Ch.14:
using System;
using System.IO;
public class DirectoryInformation
{
public static void Main()
{
string directoryName;
string[] listOfFiles;
Console.Write("Enter a folder >> ");
directoryName = Console.ReadLine();
if(Directory.Exists(directoryName))
{
Console.WriteLine("Directory exists, " +
"and it contains the following:");
listOfFiles = Directory.GetFiles(directoryName);
for(int x = 0; x < listOfFiles.Length; ++x)
Console.WriteLine(" {0}", listOfFiles[x]);
}
else
{
Console.WriteLine("Directory does not exist");
}
}
}
When I tried this code it did not work either not even if I put it into the same base folder as the directory I'm trying to find.
Path in question: C:\C#\Chapter.14\Cat Haikus
Path of Program: C:\C#\Chapter.14\TestFilesAndDirectories.cs
The path parameter is permitted to specify relative or absolute path
information. Relative path information is interpreted as relative to
the current working directory.
Source: https://msdn.microsoft.com/en-us/library/system.io.directory.exists(v=vs.110).aspx
If your input string is only a folder name like "Chapter. 14" (relative path), then this folder must exist in the path of your executable file. Like PathOfTheExecutableFile\Chapter. 14.
If the folder is in a completely different place, use absolute paths. Like C:\Users\theuser\Desktop\Chapter. 14.
Update:
Since you want to check C:\C#\Chapter.14\Cat Haikus folder, you could check if it exists using
if (Directory.Exists(#"C:\C#\Chapter.14\Cat Haikus")){
Console.WriteLine("Exists");
}
I don't know your exact folder tree structure, but if your executable file is in a subfolder of C:\C#\Chapter.14\, you could also use Directoy.GetParent() method.

How to go about searching a server for a specific file?

I am attempting to find a specific file on a web(site/server), and this file could have varying extensions depending upon the server. How would I determine the extension for a unique sever?
Example Possibilities:
website.com/list.bz2
--or--
website.com/list.gz
using System;
using System.IO;
class App
{
public static void Main()
{
string searchPath = #"c:\";
string searchPattern = "list.*";
DirectoryInfo di = new DirectoryInfo(searchPath);
FileInfo[] files = di.GetFiles(searchPattern, SearchOption.AllDirectories);
foreach (FileInfo file in files)
Console.WriteLine(file.FullName);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
If you don't have access to server and want to search it as a anonymous client, then you should search internet for google hacking.

Categories

Resources