Second commit

This commit is contained in:
ThomasCharester committed 2026-09-20 15:26:03 +03:00
commit 36840f3ad4
999 files changed
+61364

No files matched your search

@@ -0,0 +1,39 @@
//
// This file is part of the Tremble package by Tiny Goose.
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
//
using System;
using UnityEditor;
namespace TinyGoose.Tremble.Editor
{
public class FgdFileAssetPostprocessor : AssetPostprocessor
{
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (string requestFileName in importedAssets)
{
// We imported something, but not a MapFileAsset
if (AssetDatabase.GetMainAssetTypeAtPath(requestFileName) != typeof(FgdFileAsset))
continue;
bool success = false;
try
{
FgdParser.Import(requestFileName);
success = true;
}
finally
{
if (!success)
{
EditorUtility.DisplayDialog("Import Failed!", "Failed to import FGD file :(, try again!", "Okay");
}
AssetDatabase.DeleteAsset(requestFileName);
}
}
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 04f793041d9e49d7822ba5068a727bb7
timeCreated: 1724694689
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Editor/AssetProcessors/FgdFileAssetPostprocessor.cs
uploadId: 939664
@@ -0,0 +1,146 @@
//
// This file is part of the Tremble package by Tiny Goose.
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace TinyGoose.Tremble.Editor
{
public class MapFileAssetPostprocessor : AssetPostprocessor
{
private static readonly Stack<string> s_MapsToConvert = new();
private static string s_LastMapPath;
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (string requestFileName in importedAssets)
{
// We imported something, but not a MapFileAsset
if (AssetDatabase.GetMainAssetTypeAtPath(requestFileName) != typeof(MapFileAsset))
continue;
// Check we haven't already processed this map, or already plan to
string mapFileName = GetMapFileName(requestFileName);
if (File.Exists(mapFileName) || s_MapsToConvert.Contains(requestFileName))
continue;
// Add to the work queue
s_MapsToConvert.Push(requestFileName);
// Start conversion process
EditorApplication.update -= PerformConversionOnUpdate;
EditorApplication.update += PerformConversionOnUpdate;
}
}
private static string GetMapFileName(string requestFileName) => requestFileName.Replace(".asset", ".map");
private static void PerformConversionOnUpdate()
{
// Nothing left to do - stop!
if (s_MapsToConvert.Count == 0)
{
EditorApplication.update -= PerformConversionOnUpdate;
return;
}
string requestFileName = s_MapsToConvert.Pop();
// Delete the request file
string mapFileName = GetMapFileName(requestFileName);
AssetDatabase.DeleteAsset(requestFileName);
// Open new file for write
using FileStream file = File.Create(mapFileName);
using StreamWriter sw = new(file, Encoding.ASCII);
// Write a blank map, or copy template
TrembleSyncSettings trembleSyncSettings = TrembleSyncSettings.Get();
if (trembleSyncSettings.TemplateMap && trembleSyncSettings.TemplateMap.TryGetComponent(out MapDocument _))
{
string templatePath = AssetDatabase.GetAssetPath(trembleSyncSettings.TemplateMap);
using FileStream templateFile = File.OpenRead(templatePath);
using StreamReader sr = new(templateFile, Encoding.ASCII);
while (!sr.EndOfStream)
{
sw.WriteLine(sr.ReadLine());
}
sr.Close();
}
else
{
// New file
sw.WriteLine($"// Game: {TrembleConsts.GAME_NAME}");
sw.WriteLine("// Format: Quake3 (Valve)");
sw.WriteLine("// entity 0");
sw.WriteLine("{");
sw.WriteLine("\"mapversion\" \"220\"");
sw.Write("\"_tb_textures\" \"");
List<MaterialGroup> materialGroups = trembleSyncSettings.GetMaterialGroupsOrDefault();
sw.Write(String.Join(';', materialGroups.Select(mg => $"textures/{mg.Name}")));
sw.WriteLine("\"");
sw.WriteLine("\"classname\" \"worldspawn\"");
sw.WriteLine("// brush 0");
sw.WriteLine("{");
sw.WriteLine("( -128 -128 -64 ) ( -128 -127 -64 ) ( -128 -128 -63 ) __TB_empty [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 0.5 0.5");
sw.WriteLine("( -128 -128 -64 ) ( -128 -128 -63 ) ( -127 -128 -64 ) __TB_empty [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 0.5 0.5");
sw.WriteLine("( -128 -128 -64 ) ( -127 -128 -64 ) ( -128 -127 -64 ) __TB_empty [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 0.5 0.5");
sw.WriteLine("( 128 128 64 ) ( 128 129 64 ) ( 129 128 64 ) __TB_empty [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 0.5 0.5");
sw.WriteLine("( 128 128 64 ) ( 129 128 64 ) ( 128 128 65 ) __TB_empty [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 0.5 0.5");
sw.WriteLine("( 128 128 64 ) ( 128 128 65 ) ( 128 129 64 ) __TB_empty [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 0.5 0.5");
sw.WriteLine("}");
sw.WriteLine("}");
}
sw.Close();
// Open in TB?
if (EditorUtility.DisplayDialog("Map Created!", "Want to open it now in TrenchBroom?", "Yep!", "Not now..."))
{
TrembleEditorAPI.SyncToTrenchBroom();
TrenchBroomUtil.OpenWithTrenchBroom(mapFileName);
s_LastMapPath = mapFileName;
EditorApplication.update -= PromptToOpenMapOnFocus;
EditorApplication.update += PromptToOpenMapOnFocus;
}
}
private static void PromptToOpenMapOnFocus()
{
#if UNITY_2022_1_OR_NEWER
if (!EditorApplication.isFocused)
return;
#endif
EditorApplication.update -= PromptToOpenMapOnFocus;
// Place in world?
if (EditorUtility.DisplayDialog("Welcome back!", "Want to place your brand new map in this Unity scene?", "Yep!", "No thanks"))
{
AssetDatabase.Refresh();
GameObject mapPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(s_LastMapPath);
if (!mapPrefab)
{
EditorUtility.DisplayDialog("Oh no!", "Your new map failed to import. You may need to restart Unity and then reimport it. (this is a rare bug)", "Okay :(");
return;
}
GameObject mapObject = (GameObject)PrefabUtility.InstantiatePrefab(mapPrefab);
mapObject.transform.position = Vector3.zero;
}
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 6642958d88f3475995ac5c15d631af2d
timeCreated: 1707842798
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Editor/AssetProcessors/MapFileAssetPostprocessor.cs
uploadId: 939664
@@ -0,0 +1,202 @@
//
// This file is part of the Tremble package by Tiny Goose.
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
//
using System;
using System.Diagnostics;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using Debug = UnityEngine.Debug;
namespace TinyGoose.Tremble.Editor
{
public class MapLiveUpdatePostprocessor : AssetPostprocessor
{
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
if (!Application.isPlaying || !TrembleSyncSettings.Get().UseLiveUpdate)
return;
// Get list of imported GUIDs as strings
string[] importedGUIDs = importedAssets
.Select(path => AssetDatabase.GUIDFromAssetPath(path).ToString())
.ToArray();
// If we reimport during gameplay, disable and re-enable the map to refresh the collision
Scene scene = SceneManager.GetActiveScene();
MapDocument[] updatedMaps = scene.GetRootGameObjects()
.SelectMany(go => go.GetComponentsInChildren<MapDocument>())
.Where(doc => importedGUIDs.Contains(doc.OriginalAssetGuid))
.ToArray();
foreach (MapDocument map in updatedMaps)
{
Stopwatch timer = new();
timer.Start();
string assetPath = AssetDatabase.GUIDToAssetPath(map.OriginalAssetGuid);
GameObject prefabMap = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
MapDocument prefabMapDocument = prefabMap.GetComponent<MapDocument>();
// Re-instance worldspawn
GameObject newWorldspawn = InstanceNewWorldspawn(map, prefabMapDocument);
// Spawn any new objects, and transfer vars to existing
int numNewlySpawned = 0;
int numUpdated = 0;
foreach (Transform prefabChild in prefabMap.transform)
{
if (prefabChild.gameObject == prefabMapDocument.WorldspawnObject)
continue;
bool isNew = true;
foreach (Transform mapChild in map.transform)
{
if (mapChild.name != prefabChild.name)
continue;
numUpdated++;
// Object is static :( grab it from the map instead
if (mapChild.gameObject.isStatic && HasEntityMoved(mapChild, prefabChild))
{
Debug.LogWarning($"Tremble LiveUpdate: Replacing static object '{mapChild.name}' with new copy. This could cause issues!");
GameObject.Destroy(mapChild.gameObject);
break;
}
if (TryFindTrembleComponent(prefabChild.gameObject, out Component prefabComp) &&
TryFindTrembleComponent(mapChild.gameObject, out Component mapComp))
{
// Disable, copy across data, re-enable
mapComp.gameObject.SetActive(false);
CopySafeSerialisedData(from: prefabComp, to: mapComp);
mapComp.gameObject.SetActive(true);
// Handle live update callbacks
switch (mapComp)
{
case IOnLiveUpdate_Simple:
mapComp.SendMessage("OnDestroy", SendMessageOptions.DontRequireReceiver);
mapComp.SendMessage("Awake", SendMessageOptions.DontRequireReceiver);
mapComp.SendMessage("Start", SendMessageOptions.DontRequireReceiver);
break;
case IOnLiveUpdate liveUpdateable:
liveUpdateable.OnLiveUpdated();
break;
}
}
isNew = false;
break;
}
if (isNew)
{
numNewlySpawned++;
GameObject.Instantiate(prefabChild.gameObject, newWorldspawn.transform);
}
}
timer.Stop();
TryShowGameViewNotification($"Tremble LiveUpdate:\n{numNewlySpawned} new ents\n{numUpdated} updates\nin {timer.ElapsedMilliseconds}ms");
}
}
private static bool HasEntityMoved(Transform mapChild, Transform prefabChild)
{
if (Vector3.SqrMagnitude(mapChild.position - prefabChild.position) > 0f)
return true;
if (Vector3.SqrMagnitude(mapChild.rotation.eulerAngles - prefabChild.rotation.eulerAngles) > 0f)
return true;
return false;
}
private static GameObject InstanceNewWorldspawn(MapDocument map, MapDocument prefabMapDocument)
{
// Remove worldspawn
GameObject.Destroy(map.WorldspawnObject);
// Reinstance worldspawn
GameObject newWorldspawn = GameObject.Instantiate(prefabMapDocument.WorldspawnObject, map.transform, true);
newWorldspawn.transform.localPosition = Vector3.zero;
newWorldspawn.transform.localRotation = Quaternion.identity;
map.Modify(so => so.FindProperty("m_WorldspawnObject").objectReferenceValue = newWorldspawn);
newWorldspawn.transform.SetAsFirstSibling();
// Disable and re-enable - if you don't do this, Unity does not update the colliders
map.gameObject.SetActive(false);
map.gameObject.SetActive(true);
return newWorldspawn;
}
private static bool TryShowGameViewNotification(string note)
{
Type gameViewT = typeof(EditorWindow).Assembly.GetType("UnityEditor.GameView");
EditorWindow[] windows = Resources.FindObjectsOfTypeAll(gameViewT).Cast<EditorWindow>().ToArray();
foreach (EditorWindow win in windows)
{
win.ShowNotification(new(note), 1f);
}
return windows.Length > 0;
}
private static bool TryFindTrembleComponent(GameObject go, out Component component)
{
foreach (Component c in go.GetComponents<Component>())
{
if (!c.GetType().HasCustomAttribute<EntityAttributeBase>())
continue;
component = c;
return true;
}
component = default;
return false;
}
private static void CopySafeSerialisedData(Component from, Component to)
{
// For now, we just copy simple data - floats, colours, vectors etc.
// References and assets are a bit spoopier and we can't do too much there sadly!
SerializedObject fromObject = new(from);
SerializedObject toObject = new(to);
SerializedProperty iterator = fromObject.GetIterator();
iterator.NextVisible(true);
do
{
if (iterator.propertyType
is SerializedPropertyType.ObjectReference
or SerializedPropertyType.ManagedReference)
{
if (iterator.objectReferenceValue is Component)
continue;
}
toObject.CopyFromSerializedProperty(iterator);
} while (iterator.NextVisible(false));
toObject.ApplyModifiedPropertiesWithoutUndo();
// Also copy basic positioning information
Transform fromTransform = from.transform;
Transform toTransform = to.transform;
toTransform.localPosition = fromTransform.localPosition;
toTransform.localRotation = fromTransform.localRotation;
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: de0e937a05b84f928a4e0c9ff6d32146
timeCreated: 1708536137
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Editor/AssetProcessors/MapLiveUpdatePostprocessor.cs
uploadId: 939664
@@ -0,0 +1,22 @@
//
// This file is part of the Tremble package by Tiny Goose.
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
//
using UnityEditor;
namespace TinyGoose.Tremble.Editor
{
public class TrembleCacheDropperPostprocessor : AssetPostprocessor
{
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
if (!TrembleSyncSettings.Get().AutomaticallyReimportWhenDependencyChanges)
{
MapHierarchyIcons.ResetModifiedTimes();
}
EditorLoaderAdaptor.DropCachedAssetPaths();
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 5e639ddfe46c434eb843b1bd7ee8259c
timeCreated: 1713956404
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Editor/AssetProcessors/TrembleCacheDropperPostprocessor.cs
uploadId: 939664
@@ -0,0 +1,38 @@
//
// This file is part of the Tremble package by Tiny Goose.
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
//
using UnityEditor;
namespace TinyGoose.Tremble.Editor
{
public class WadFileAssetPostprocessor : AssetPostprocessor
{
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (string requestFileName in importedAssets)
{
// We imported something, but not a WadFileAsset
if (AssetDatabase.GetMainAssetTypeAtPath(requestFileName) != typeof(WadFileAsset))
continue;
bool success = false;
try
{
WadAssetImporterEditor.Extract(requestFileName);
success = true;
}
finally
{
if (!success)
{
EditorUtility.DisplayDialog("Import Failed!", "Failed to import WAD file :(, try again!", "Okay");
}
AssetDatabase.DeleteAsset(requestFileName);
}
}
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c83945126e2c4fa9aa009f1463530bad
timeCreated: 1722936486
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Editor/AssetProcessors/WadFileAssetPostprocessor.cs
uploadId: 939664