Kaynağa Gözat

[prefab] Remote edit (#279)

* [prefab] Remote editing POC

* [prefab] Remote edition functional

* [prefab] Remote edition protocol simplification
Valden 2 gün önce
ebeveyn
işleme
0502e485ee

+ 64 - 13
hide/view/Prefab.hx

@@ -194,21 +194,29 @@ class Prefab extends hide.view.FileView {
 	public var properties(get, null):  hide.comp.PropsEditor;
 	function get_properties() return sceneEditor.properties;
 
+	var remoteEditMode = false;
+
 
 	override function new(state) {
 		super(state);
 
-		var config = hide.Config.loadForFile(ide, ide.getPath(state.path));
-		var matLibs : Array<Dynamic> = config.get("materialLibraries");
-		if (matLibs != null) {
-			for (lib in matLibs) {
-				if (state.path == lib.path) {
-					matLibPath = lib.path;
-					renameMatsHistory = [];
-					break;
+		if (state.path != null) {
+			var config = hide.Config.loadForFile(ide, ide.getPath(state.path));
+			var matLibs : Array<Dynamic> = config.get("materialLibraries");
+			if (matLibs != null) {
+				for (lib in matLibs) {
+					if (state.path == lib.path) {
+						matLibPath = lib.path;
+						renameMatsHistory = [];
+						break;
+					}
 				}
 			}
 		}
+
+		if ((state:Dynamic).remoteId != null) {
+			remoteEditMode = true;
+		}
 	}
 
 	function createData() {
@@ -248,9 +256,10 @@ class Prefab extends hide.view.FileView {
 	override function onDisplay() {
 		if( sceneEditor != null ) sceneEditor.dispose();
 		createData();
-		var content = sys.io.File.getContent(getPath());
-		currentSign = ide.makeSignature(content);
-
+		if (!remoteEditMode) {
+			var content = sys.io.File.getContent(getPath());
+			currentSign = ide.makeSignature(content);
+		}
 
 		element.html('
 			<div class="flex vertical">
@@ -436,7 +445,9 @@ class Prefab extends hide.view.FileView {
 	}
 
 	public function onSceneReady() {
-		data = hxd.res.Loader.currentInstance.load(state.path).toPrefab().loadBypassCache();
+		if (state.path != null) {
+			data = hxd.res.Loader.currentInstance.load(state.path).toPrefab().loadBypassCache();
+		}
 		sceneEditor.setPrefab(cast data);
 
 		refreshSceneFilters();
@@ -585,6 +596,22 @@ class Prefab extends hide.view.FileView {
 		if( !canSave() )
 			return;
 
+		if (remoteEditMode) {
+			currentSign = Std.string(Std.parseInt(currentSign) + 1);
+			var localVersion = currentSign;
+			var data = data.children[0].serialize();
+
+			@:privateAccess hide.view.RemoteConsoleView.rcmd?.sendCommand("remotePrefab", {kind: hrt.impl.RemoteConsole.RemotePrefabActionKind.Update, data: data, id: (state:Dynamic).remoteId}, (result: {data: String}) -> {
+				if (result?.data == "ok") {
+					if (localVersion == currentSign) {
+						modified = false;
+					}
+				}
+			});
+
+			return;
+		}
+
 		// Save render props
 		if (Ide.inst.currentConfig.get("sceneeditor.renderprops.edit", false) && sceneEditor.renderPropsRoot != null)
 			sceneEditor.renderPropsRoot.save();
@@ -610,7 +637,6 @@ class Prefab extends hide.view.FileView {
 		// 	renameMatsHistory = [];
 		// }
 	}
-
 	function saveMatLibsRenames(oldName : String, newName : String, prefab : hrt.prefab.Prefab) {
 		function renameContent(content:Dynamic) {
 			var visited = new Array<Dynamic>();
@@ -746,6 +772,26 @@ class Prefab extends hide.view.FileView {
 		}
 	}
 
+	override function getPath() {
+		if (state.path == null)
+			return null;
+		return ide.getPath(state.path);
+	}
+
+	override function getTitle() {
+		var name = "Prefab Editor";
+		if (state.path != null) {
+			var parts = state.path.split("/");
+			if( parts[parts.length - 1] == "" ) parts.pop(); // directory
+			while( parts.length > 2 ) parts.shift();
+			name = parts.join(" / ");
+		}
+		else if (remoteEditMode) {
+			name = "Remote Prefab Editor";
+		}
+		return name+(modified?" *":"");
+	}
+
 	function initGraphicsFilters() {
 		for (typeid in graphicsFilters.keys())
 		{
@@ -753,6 +799,11 @@ class Prefab extends hide.view.FileView {
 		}
 	}
 
+	public static function getRemoteView(remoteId: String) : hide.view.Prefab {
+		var ide = hide.Ide.inst;
+		return ide.getViews(hide.view.Prefab).find((v) -> (v.state:Dynamic).remoteId == remoteId);
+	}
+
 	function initSceneFilters() {
 		for (typeid in sceneFilters.keys())
 		{

+ 46 - 0
hrt/impl/RemoteConsole.hx

@@ -5,6 +5,17 @@ typedef RemoteMenuAction = {
 	?cdbSheet : String,
 }
 
+typedef RemotePrefabAction = {
+	kind: RemotePrefabActionKind,
+	data: Dynamic,
+	id: String,
+};
+
+enum abstract RemotePrefabActionKind(String) {
+	var Open; /** Game -> Hide : Request open of the prefab data contained in data**/
+	var Update; /** Hide -> Game : Update the opened prefab in the game with the new provided data**/
+}
+
 /**
 	A simple socket-based local communication channel (plaintext and unsafe),
 	aim at communicate between 2 programs (e.g. Hide and a HL game).
@@ -335,6 +346,41 @@ class RemoteConsoleConnection {
 		return -1;
 	}
 
+	/**
+		Game <-> Hide editor messages for remote prefab edition
+	**/
+	@cmd function remotePrefab(args: RemotePrefabAction) : {data: Dynamic} {
+		switch (args.kind) {
+			case Open:
+				#if editor
+				hide.Ide.inst.open("hide.view.Prefab", {remoteId: args.id}, null, (v) -> @:privateAccess {
+					var prefabView : hide.view.Prefab = cast v;
+					var toEdit = hrt.prefab.Prefab.createFromDynamic(args.data);
+					prefabView.createData();
+					toEdit.parent = prefabView.data;
+					prefabView.sceneEditor.delayReady(() -> {
+						prefabView.sceneEditor.setPrefab(cast prefabView.data);
+						prefabView.sceneEditor.selectElements([toEdit], NoHistory);
+						haxe.Timer.delay(() -> {
+							prefabView.sceneEditor.focusObjects([toEdit.findFirstLocal3d()]);
+						},0);
+					});
+				});
+				#end
+				return null;
+			case Update:
+				#if !editor
+				var cb = @:privateAccess RemoteTools.remotePrefabsCallbacks.get(args.id);
+				if (cb != null) {
+					cb(args.data);
+					return {data: "ok"};
+				}
+				return {data: null};
+				#end
+				return null;
+		}
+	}
+
 #if editor
 	// ----- Hide ------
 

+ 7 - 0
hrt/impl/RemoteTools.hx

@@ -1,5 +1,7 @@
 package hrt.impl;
 
+typedef RemotePrefabCallback = (data: Dynamic) -> Void;
+
 /**
 	A helper class to use a RemoteConsole in game.
  */
@@ -12,6 +14,7 @@ class RemoteTools {
 	static var lastUpdate : Float;
 	static var onConnected : Bool -> Void;
 	static var menuActions : Array<{ action : RemoteConsole.RemoteMenuAction, f : (id:String) -> Int}> = [];
+	static var remotePrefabsCallbacks: Map<String, RemotePrefabCallback> = [];
 
 	public static function autoConnect( ?onConnected : Bool -> Void ) {
 		RemoteTools.onConnected = onConnected;
@@ -126,4 +129,8 @@ class RemoteTools {
 		rc?.sendCommand("open", { file : file, selectExpr : selectExpr });
 	}
 
+	public static function editPrefab( prefab: hrt.prefab.Prefab, id: String, callback: RemotePrefabCallback) {
+		rc?.sendCommand("remotePrefab", {kind: hrt.impl.RemoteConsole.RemotePrefabActionKind.Open, data: @:privateAccess prefab.serialize(), id: id});
+		remotePrefabsCallbacks.set(id, callback);
+	}
 }

+ 14 - 10
hrt/prefab/Object3D.hx

@@ -229,6 +229,20 @@ class Object3D extends Prefab {
 		return [];
 	}
 
+	override function editorRemoveInstanceObjects() : Void {
+		if (local3d != null)
+			local3d.remove();
+		#if editor
+		if (editorIcon != null)
+			editorIcon.remove();
+		#end
+		local3d = null;
+		#if editor
+		editorIcon = null;
+		#end
+		super.editorRemoveInstanceObjects();
+	}
+
 #if editor
 	override function setSelected(b:Bool):Bool {
 		if (local3d == null)
@@ -476,16 +490,6 @@ class Object3D extends Prefab {
 		return int;
 	}
 
-	override function editorRemoveInstanceObjects() : Void {
-		if (local3d != null)
-			local3d.remove();
-		if (editorIcon != null)
-			editorIcon.remove();
-		local3d = null;
-		editorIcon = null;
-		super.editorRemoveInstanceObjects();
-	}
-
 	override function edit( ctx : hide.prefab.EditContext ) {
 		var props = new hide.Element('
 			<div class="group" name="Position">

+ 18 - 17
hrt/prefab/Prefab.hx

@@ -615,6 +615,24 @@ class Prefab {
 
 	// Editor API
 
+	/**
+		Called by the editor to remove the object created by this prefab tree
+	**/
+	function editorRemoveObjects() : Void {
+		for (child in children) {
+			child.editorRemoveObjects();
+		}
+		editorRemoveInstanceObjects();
+		dispose();
+	}
+
+	/**
+		Called by the editor to remove the objects created by this prefab but not its children.
+	**/
+	function editorRemoveInstanceObjects() : Void {
+	}
+
+
 	#if editor
 	/**
 		Allows to customize how the prefab object is displayed / handled within Hide
@@ -638,23 +656,6 @@ class Prefab {
 		return true;
 	}
 
-	/**
-		Called by the editor to remove the object created by this prefab tree
-	**/
-	function editorRemoveObjects() : Void {
-		for (child in children) {
-			child.editorRemoveObjects();
-		}
-		editorRemoveInstanceObjects();
-		dispose();
-	}
-
-	/**
-		Called by the editor to remove the objects created by this prefab but not its children.
-	**/
-	function editorRemoveInstanceObjects() : Void {
-	}
-
 	/**
 		Called by the editor when a child of this object gets added, rebuild or removed.
 	**/

+ 12 - 0
hrt/prefab/Reference.hx

@@ -234,6 +234,18 @@ class Reference extends Object3D {
 		#end
 	}
 
+	override function editorRemoveInstanceObjects() {
+		super.editorRemoveInstanceObjects();
+		// Clean cache to force proper ref reloading
+		@:privateAccess if (source != null) {
+			var cachedPrefab = Std.downcast(hxd.res.Loader.currentInstance.cache.get(source), hrt.prefab.Resource);
+			if (cachedPrefab != null) {
+				cachedPrefab.prefab = null;
+			}
+		}
+		refInstance = null;
+	}
+
 	override public function findRec<T:Prefab>(?cl: Class<T>, ?filter : T -> Bool, followRefs : Bool = false, includeDisabled: Bool = true) : Null<T> {
 		if (!includeDisabled && !enabled)
 			return null;