فهرست منبع

RemoteConsole: use Hide as the server

Yuxiao Mao 7 ماه پیش
والد
کامیت
5be805f560
4فایلهای تغییر یافته به همراه207 افزوده شده و 164 حذف شده
  1. 14 11
      bin/style.css
  2. 17 13
      bin/style.less
  3. 83 108
      hide/view/RemoteConsoleView.hx
  4. 93 32
      hrt/impl/RemoteConsole.hx

+ 14 - 11
bin/style.css

@@ -3484,6 +3484,20 @@ div.gradient-box {
   padding-right: 2px;
   padding-right: 2px;
   cursor: pointer;
   cursor: pointer;
 }
 }
+.remoteconsole .logs {
+  margin-top: 2px;
+  margin-bottom: 2px;
+  background-color: #151515;
+  height: 20px;
+  overflow-y: auto;
+}
+.remoteconsole .logs p {
+  margin-block-start: 0px;
+  margin-block-end: 0px;
+}
+.remoteconsole .logs .error {
+  color: #ff2828;
+}
 .remoteconsole .remoteconsole-panel {
 .remoteconsole .remoteconsole-panel {
   border: 2px dashed #444444;
   border: 2px dashed #444444;
   border-radius: 4px;
   border-radius: 4px;
@@ -3496,18 +3510,7 @@ div.gradient-box {
   float: right;
   float: right;
 }
 }
 .remoteconsole .remoteconsole-panel .logs {
 .remoteconsole .remoteconsole-panel .logs {
-  margin-top: 2px;
-  margin-bottom: 2px;
-  background-color: #151515;
   height: 100px;
   height: 100px;
-  overflow-y: auto;
-}
-.remoteconsole .remoteconsole-panel .logs p {
-  margin-block-start: 0px;
-  margin-block-end: 0px;
-}
-.remoteconsole .remoteconsole-panel .logs .error {
-  color: #ff2828;
 }
 }
 .remoteconsole .remoteconsole-panel .commands input {
 .remoteconsole .remoteconsole-panel .commands input {
   margin-left: 2px;
   margin-left: 2px;

+ 17 - 13
bin/style.less

@@ -4070,6 +4070,23 @@ div.gradient-box {
 		cursor: pointer;
 		cursor: pointer;
 	}
 	}
 
 
+	.logs {
+		margin-top: 2px;
+		margin-bottom: 2px;
+		background-color: #151515;
+		height: 20px;
+		overflow-y: auto;
+
+		p {
+			margin-block-start: 0px;
+			margin-block-end: 0px;
+		}
+
+		.error {
+			color: #ff2828;
+		}
+	}
+
 	.remoteconsole-panel {
 	.remoteconsole-panel {
 		border: 2px dashed #444444;
 		border: 2px dashed #444444;
 		border-radius: 4px;
 		border-radius: 4px;
@@ -4083,20 +4100,7 @@ div.gradient-box {
 		}
 		}
 
 
 		.logs {
 		.logs {
-			margin-top: 2px;
-			margin-bottom: 2px;
-			background-color: #151515;
 			height: 100px;
 			height: 100px;
-			overflow-y: auto;
-
-			p {
-				margin-block-start: 0px;
-				margin-block-end: 0px;
-			}
-
-			.error {
-				color: #ff2828;
-			}
 		}
 		}
 
 
 		.commands {
 		.commands {

+ 83 - 108
hide/view/RemoteConsoleView.hx

@@ -4,21 +4,20 @@ package hide.view;
 	props.json configuration (all champs are optional):
 	props.json configuration (all champs are optional):
 	```json
 	```json
 	"remoteconsole": {
 	"remoteconsole": {
-		"panels": [
-			{
-				"host": "127.0.0.2",
-				"port": 40002,
-				"commands": [
-					"dump"
-				]
-			}
+		"host": "127.0.0.2",
+		"port": 40002,
+		"commands": [
+			"dump",
+			"custom"
 		]
 		]
 	},
 	},
 	```
 	```
  */
  */
 class RemoteConsoleView extends hide.ui.View<{}> {
 class RemoteConsoleView extends hide.ui.View<{}> {
+	static var rcmd : hrt.impl.RemoteConsole;
 	var panels : Array<RemoteConsolePanel>;
 	var panels : Array<RemoteConsolePanel>;
 	var panelsView : Element;
 	var panelsView : Element;
+	var logsView : Element;
 	var newPanelBtn : Element;
 	var newPanelBtn : Element;
 	public var statusBarIcons : Element;
 	public var statusBarIcons : Element;
 
 
@@ -28,10 +27,38 @@ class RemoteConsoleView extends hide.ui.View<{}> {
 	}
 	}
 
 
 	override function onDisplay() {
 	override function onDisplay() {
+		var pconfig = config.get("remoteconsole");
+		var host = pconfig?.host ?? hrt.impl.RemoteConsole.DEFAULT_HOST;
+		var port = pconfig?.port ?? hrt.impl.RemoteConsole.DEFAULT_PORT;
 		new Element('
 		new Element('
 		<div class="remoteconsole hide-scroll">
 		<div class="remoteconsole hide-scroll">
+			<div class="connect">
+				<input type="button" id="startServerBtn" value="Start Server"/>
+				<input type="button" id="stopServerBtn" value="Stop Server"/>
+				<label for="connectHost">Host IP</label>
+				<input type="text" id="connectHost" value="$host:$port" disabled/>
+				<div class="logs">
+				</div>
+			</div>
 		</div>').appendTo(element);
 		</div>').appendTo(element);
+		element.find("#startServerBtn").on('click', function(e) {
+			if( rcmd != null )
+				rcmd.close();
+			rcmd = new hrt.impl.RemoteConsole(port, host);
+			rcmd.log = (msg) -> log(msg);
+			rcmd.logError = (msg) -> log(msg, true);
+			rcmd.startServer(function(c) {
+				addPanel(c);
+			});
+		});
+		element.find("#stopServerBtn").on('click', function(e) {
+			if( rcmd != null )
+				rcmd.close();
+			log("Server stopped");
+		});
+
 		panelsView = element.find(".remoteconsole");
 		panelsView = element.find(".remoteconsole");
+		logsView = element.find(".logs");
 		statusBarIcons = new Element('<div></div>');
 		statusBarIcons = new Element('<div></div>');
 		hide.Ide.inst.addStatusIcon(statusBarIcons);
 		hide.Ide.inst.addStatusIcon(statusBarIcons);
 		addPanel();
 		addPanel();
@@ -61,11 +88,19 @@ class RemoteConsoleView extends hide.ui.View<{}> {
 		if( panelsView != null )
 		if( panelsView != null )
 			panelsView.remove();
 			panelsView.remove();
 		panelsView = null;
 		panelsView = null;
+		logsView = null;
 		if( statusBarIcons != null )
 		if( statusBarIcons != null )
 			statusBarIcons.remove();
 			statusBarIcons.remove();
 		statusBarIcons = null;
 		statusBarIcons = null;
 	}
 	}
 
 
+	public function log( msg : String, error : Bool = false ) {
+		var el = new Element('<p>${StringTools.htmlEscape(msg)}</p>').appendTo(logsView);
+		if( error )
+			el.addClass("error");
+		logsView.scrollTop(logsView.get(0).scrollHeight);
+	}
+
 	function refreshNewPanelButton() {
 	function refreshNewPanelButton() {
 		if( newPanelBtn != null )
 		if( newPanelBtn != null )
 			newPanelBtn.remove();
 			newPanelBtn.remove();
@@ -78,12 +113,24 @@ class RemoteConsoleView extends hide.ui.View<{}> {
 		});
 		});
 	}
 	}
 
 
-	function addPanel() {
-		var pconfigs = config.get("remoteconsole")?.panels;
-		var pconfig = pconfigs != null ? pconfigs[panels.length] : null;
-		var panel = new RemoteConsolePanel(this, pconfig?.host, pconfig?.port, pconfig?.commands);
-		panel.element.appendTo(panelsView);
-		panels.push(panel);
+	function addPanel( ?c : hrt.impl.RemoteConsole.RemoteConsoleConnection ) {
+		var panel = null;
+		if( c != null ) {
+			// Find the first empty or disconnected panel
+			for( p in panels ) {
+				if( p.connection == null || p.connection == c || !p.connection.isConnected() ) {
+					p.connection = c;
+					panel = p;
+					break;
+				}
+			}
+		}
+		if( panel == null ) {
+			var pconfig = config.get("remoteconsole");
+			var panel = new RemoteConsolePanel(this, c, pconfig?.commands);
+			panel.element.appendTo(panelsView);
+			panels.push(panel);
+		}
 		refreshNewPanelButton();
 		refreshNewPanelButton();
 	}
 	}
 
 
@@ -110,102 +157,47 @@ class RemoteConsoleView extends hide.ui.View<{}> {
 
 
 class RemoteConsolePanel extends hide.comp.Component {
 class RemoteConsolePanel extends hide.comp.Component {
 	var view : RemoteConsoleView;
 	var view : RemoteConsoleView;
+	public var connection(default, set) : hrt.impl.RemoteConsole.RemoteConsoleConnection;
 	var statusBarIcon : Element;
 	var statusBarIcon : Element;
 	var statusIcon : Element;
 	var statusIcon : Element;
-	var handler : RemoteCommandHandler;
-	var rcmd : hrt.impl.RemoteConsole;
-	public function new( view : RemoteConsoleView, host : String, port : Int, commands : Array<String> ) {
+	public function new( view : RemoteConsoleView, connection : Null<hrt.impl.RemoteConsole.RemoteConsoleConnection>, commands : Null<Array<String>> ) {
 		super(null, null);
 		super(null, null);
 		this.view = view;
 		this.view = view;
-		this.handler = new RemoteCommandHandler();
+		this.connection = connection;
 		element = new Element('
 		element = new Element('
 		<div class="remoteconsole-panel">
 		<div class="remoteconsole-panel">
 			<div class="controls">
 			<div class="controls">
 				<div class="ico ico-dot-circle-o" id="statusIcon" style="color: darkgray; cursor:default;" title="Not connected"></div>
 				<div class="ico ico-dot-circle-o" id="statusIcon" style="color: darkgray; cursor:default;" title="Not connected"></div>
 				<div class="ico ico-close" id="closeBtn" title="Close panel"></div>
 				<div class="ico ico-close" id="closeBtn" title="Close panel"></div>
 			</div>
 			</div>
-			<div class="connect">
-				<input type="button" id="connectBtn" value="Connect"/>
-				<input type="button" id="disconnectBtn" value="Disconnect"/>
-				<label for="connectHost">Host</label>
-				<input type="text" id="connectHost"/>
-				<label for="connectPort">Port</label>
-				<input type="text" id="connectPort" type="number"/>
-			</div>
 			<div class="logs">
 			<div class="logs">
 			</div>
 			</div>
 			<div class="commands">
 			<div class="commands">
 			</div>
 			</div>
 		</div>
 		</div>
 		');
 		');
-
 		this.statusBarIcon = new Element('
 		this.statusBarIcon = new Element('
 			<div class="ico ico-dot-circle-o" style="color: darkgray; cursor:default;" title="[Remote Console] Not connected"></div>'
 			<div class="ico ico-dot-circle-o" style="color: darkgray; cursor:default;" title="[Remote Console] Not connected"></div>'
 		).appendTo(view.statusBarIcons);
 		).appendTo(view.statusBarIcons);
 		this.statusIcon = element.find("#statusIcon");
 		this.statusIcon = element.find("#statusIcon");
-
 		element.find("#closeBtn").on('click', function(e) {
 		element.find("#closeBtn").on('click', function(e) {
 			this.statusBarIcon.remove();
 			this.statusBarIcon.remove();
 			view.removePanel(this);
 			view.removePanel(this);
 		});
 		});
-
-		var connectHost = element.find("#connectHost");
-		connectHost.val(host ?? hrt.impl.RemoteConsole.DEFAULT_HOST);
-		function checkHost() {
-			var host = connectHost.val();
-			js.node.Dns.lookup(host, function(err, address, family) {
-				if( err != null ) {
-					log('Invalid host ($host): ${err.message}', true);
-					return;
-				}
-				log('Host resolved ($host): $address');
-				connectHost.val(address);
-			});
-		}
-		connectHost.keydown(function(e) {
-			if (e.key == 'Enter') checkHost();
-		});
-		connectHost.focusout(function(e) {
-			checkHost();
-		});
-
-		var connectPort = element.find("#connectPort");
-		connectPort.val(port ?? hrt.impl.RemoteConsole.DEFAULT_PORT);
-		function checkPort() {
-			var port = Std.int(connectPort.val());
-			if( port < 0 )
-				port = isConnected() ? rcmd.port : hrt.impl.RemoteConsole.DEFAULT_PORT;
-			connectPort.val(port);
-		}
-		connectPort.keydown(function(e) {
-			if (e.key == 'Enter') checkPort();
-		});
-		connectPort.focusout(function(e) {
-			checkPort();
-		});
-
-		element.find("#connectBtn").on('click', function(e) {
-			close();
-			var host = element.find("#connectHost").val();
-			var port = Std.parseInt(element.find("#connectPort").val());
-			rcmd = new hrt.impl.RemoteConsole(port, host);
-			rcmd.log = (msg) -> log(msg);
-			rcmd.logError = (msg) -> log(msg, true);
-			rcmd.registerCommands(handler);
-			rcmd.connect(function(b) {
-				refreshStatusIcon();
-			});
-		});
-
-		element.find("#disconnectBtn").on('click', function(e) {
-			close();
-			refreshStatusIcon();
-		});
-
 		var commandsList = commands ?? ["dump", "prof", "custom"];
 		var commandsList = commands ?? ["dump", "prof", "custom"];
 		for( name in commandsList )
 		for( name in commandsList )
 			addCommandElement(name);
 			addCommandElement(name);
 	}
 	}
+	function set_connection( c ) {
+		if( c != null ) {
+			c.onClose = () -> refreshStatusIcon();
+			c.log = (msg) -> log(msg);
+			c.logError = (msg) -> log(msg, true);
+		}
+		connection = c;
+		refreshStatusIcon();
+		return connection;
+	}
 	function addCommandElement( name : String ) {
 	function addCommandElement( name : String ) {
 		var c = RemoteConsoleView.commandViews.get(name);
 		var c = RemoteConsoleView.commandViews.get(name);
 		if( c == null ) {
 		if( c == null ) {
@@ -216,11 +208,13 @@ class RemoteConsolePanel extends hide.comp.Component {
 		comp.element.appendTo(element.find(".commands"));
 		comp.element.appendTo(element.find(".commands"));
 	}
 	}
 	function refreshStatusIcon() {
 	function refreshStatusIcon() {
+		if( statusBarIcon == null || statusIcon == null )
+			return;
 		if( isConnected() ) {
 		if( isConnected() ) {
 			statusBarIcon.css("color", "DarkGreen");
 			statusBarIcon.css("color", "DarkGreen");
-			statusBarIcon.prop("title", "[Remote console] Connected to " + rcmd.host + ":" + rcmd.port);
+			statusBarIcon.prop("title", "[Remote console] Connected");
 			statusIcon.css("color", "DarkGreen");
 			statusIcon.css("color", "DarkGreen");
-			statusIcon.prop("title", "Connected to " + rcmd.host + ":" + rcmd.port);
+			statusIcon.prop("title", "Connected");
 		} else {
 		} else {
 			statusBarIcon.css("color", "#c10000");
 			statusBarIcon.css("color", "#c10000");
 			statusBarIcon.prop("title", "[Remote console] Disconnected");
 			statusBarIcon.prop("title", "[Remote console] Disconnected");
@@ -229,14 +223,14 @@ class RemoteConsolePanel extends hide.comp.Component {
 		}
 		}
 	}
 	}
 	public function close() {
 	public function close() {
-		if( rcmd != null ) {
-			rcmd.close();
+		if( connection != null ) {
+			connection.close();
 			log("Disconnected");
 			log("Disconnected");
 		}
 		}
-		rcmd = null;
+		connection = null;
 	}
 	}
 	public function isConnected() {
 	public function isConnected() {
-		return rcmd != null && rcmd.isConnected();
+		return connection != null && connection.isConnected();
 	}
 	}
 	public function log( msg : String, error : Bool = false ) {
 	public function log( msg : String, error : Bool = false ) {
 		var logsView = element.find(".logs");
 		var logsView = element.find(".logs");
@@ -247,31 +241,12 @@ class RemoteConsolePanel extends hide.comp.Component {
 	}
 	}
 	public function sendCommand( cmd : String, ?args : Dynamic, ?onResult : Dynamic -> Void ) {
 	public function sendCommand( cmd : String, ?args : Dynamic, ?onResult : Dynamic -> Void ) {
 		if( isConnected() )
 		if( isConnected() )
-			rcmd.sendCommand(cmd, args, onResult);
+			connection.sendCommand(cmd, args, onResult);
 		else
 		else
 			log("sendCommand not available: no connection.", true);
 			log("sendCommand not available: no connection.", true);
 	}
 	}
 }
 }
 
 
-@:keep
-@:rtti
-class RemoteCommandHandler {
-	public function new() {
-	}
-	@cmd function open( args : { file : String, line : Int, column : Int, cdbsheet : String } ) {
-		if( args == null )
-			return;
-		if( args.cdbsheet != null ) {
-			var sheet = hide.Ide.inst.database.getSheet(args.cdbsheet);
-			hide.Ide.inst.open("hide.view.CdbTable", {}, function(view) {
-				Std.downcast(view,hide.view.CdbTable).goto(sheet,args.line,args.column);
-			});
-		} else {
-			hide.Ide.inst.openFile(args.file);
-		}
-	}
-}
-
 class RemoteConsoleCommand extends hide.comp.Component {
 class RemoteConsoleCommand extends hide.comp.Component {
 	var panel : RemoteConsolePanel;
 	var panel : RemoteConsolePanel;
 	public function new( panel : RemoteConsolePanel ) {
 	public function new( panel : RemoteConsolePanel ) {

+ 93 - 32
hrt/impl/RemoteConsole.hx

@@ -4,55 +4,53 @@ package hrt.impl;
 	A simple socket-based local communication channel,
 	A simple socket-based local communication channel,
 	aim at communicate between 2 programs (e.g. Hide and a HL game).
 	aim at communicate between 2 programs (e.g. Hide and a HL game).
 
 
-	Usage:
+	Usage in game:
 	```haxe
 	```haxe
 	var rcmd = new hrt.impl.RemoteConsole();
 	var rcmd = new hrt.impl.RemoteConsole();
 	// rcmd.log = (msg) -> logToUI(msg);
 	// rcmd.log = (msg) -> logToUI(msg);
 	// rcmd.logError = (msg) -> logErrorToUI(msg);
 	// rcmd.logError = (msg) -> logErrorToUI(msg);
 	rcmd.registerCommands(handler);
 	rcmd.registerCommands(handler);
-	rcmd.connect(); // or rcmd.startServer()
-	rc.sendCommand("log", "Hello!", function(r) {});
+	rcmd.connect();
+	rcmd.sendCommand("log", "Hello!", function(r) {});
 	```
 	```
  */
  */
-@:keep
-@:rtti
 class RemoteConsole {
 class RemoteConsole {
 	public static var DEFAULT_HOST : String = "127.0.0.1";
 	public static var DEFAULT_HOST : String = "127.0.0.1";
 	public static var DEFAULT_PORT : Int = 40001;
 	public static var DEFAULT_PORT : Int = 40001;
+	public static var SILENT_CONNECT : Bool = true;
 
 
-	var UID : Int = 0;
 	public var host : String;
 	public var host : String;
 	public var port : Int;
 	public var port : Int;
 	var sock : hxd.net.Socket;
 	var sock : hxd.net.Socket;
-	var cSocks : Array<hxd.net.Socket>;
-	var waitReply : Map<Int, Dynamic->Void>;
+	var cSocks : Array<RemoteConsoleConnection>;
 
 
 	public function new( ?port : Int, ?host : String ) {
 	public function new( ?port : Int, ?host : String ) {
 		this.host = host ?? DEFAULT_HOST;
 		this.host = host ?? DEFAULT_HOST;
 		this.port = port ?? DEFAULT_PORT;
 		this.port = port ?? DEFAULT_PORT;
-		registerCommands(this);
 	}
 	}
 
 
-	public function startServer() {
+	public function startServer( ?onClient : RemoteConsoleConnection->Void ) {
 		close();
 		close();
 		sock = new hxd.net.Socket();
 		sock = new hxd.net.Socket();
 		sock.onError = function(msg) {
 		sock.onError = function(msg) {
 			logError("Socket Error: " + msg);
 			logError("Socket Error: " + msg);
 			close();
 			close();
 		}
 		}
-		cSocks = [];
 		sock.bind(host, port, function(s) {
 		sock.bind(host, port, function(s) {
-			cSocks.push(s);
+			var connection = new RemoteConsoleConnection(this, s);
+			cSocks.push(connection);
 			s.onError = function(msg) {
 			s.onError = function(msg) {
-				logError("Client error: " + msg);
-				if( s != null )
-					s.close();
-				if( s != null && cSocks != null ) {
-					cSocks.remove(s);
+				connection.logError("Client error: " + msg);
+				connection.close();
+				if( cSocks != null ) {
+					cSocks.remove(connection);
 				}
 				}
+				connection = null;
 			}
 			}
-			s.onData = () -> handleOnData(s);
-			log("Client connected");
+			s.onData = () -> connection.handleOnData();
+			if( onClient != null )
+				onClient(connection);
+			connection.log("Client connected");
 		}, 1);
 		}, 1);
 		log('Server started at $host:$port');
 		log('Server started at $host:$port');
 	}
 	}
@@ -61,18 +59,22 @@ class RemoteConsole {
 		close();
 		close();
 		sock = new hxd.net.Socket();
 		sock = new hxd.net.Socket();
 		sock.onError = function(msg) {
 		sock.onError = function(msg) {
-			logError("Socket Error: " + msg);
+			if( !SILENT_CONNECT )
+				logError("Socket Error: " + msg);
 			close();
 			close();
 			if( onConnected != null )
 			if( onConnected != null )
 				onConnected(false);
 				onConnected(false);
 		}
 		}
-		sock.onData = () -> handleOnData(sock);
+		var connection = new RemoteConsoleConnection(this, sock);
+		cSocks.push(connection);
+		sock.onData = () -> connection.handleOnData();
 		sock.connect(host, port, function() {
 		sock.connect(host, port, function() {
 			log("Connected to server");
 			log("Connected to server");
 			if( onConnected != null )
 			if( onConnected != null )
 				onConnected(true);
 				onConnected(true);
 		});
 		});
-		log('Connecting to $host:$port');
+		if( !SILENT_CONNECT )
+			log('Connecting to $host:$port');
 	}
 	}
 
 
 	public function close() {
 	public function close() {
@@ -83,16 +85,65 @@ class RemoteConsole {
 		if( cSocks != null ) {
 		if( cSocks != null ) {
 			for( s in cSocks )
 			for( s in cSocks )
 				s.close();
 				s.close();
-			cSocks = null;
 		}
 		}
+		cSocks = [];
+	}
+
+	public function isConnected() {
+		return sock != null;
+	}
+
+	public dynamic function log( msg : String ) {
+		trace(msg);
+	}
+
+	public dynamic function logError( msg : String ) {
+		trace('[Error] $msg');
+	}
+
+	public function sendCommand( cmd : String, ?args : Dynamic, ?onResult : Dynamic -> Void ) {
+		if( cSocks.length == 0 ) {
+			// Ignore send when not really connected
+		} else if( cSocks.length == 1 ) {
+			cSocks[0].sendCommand(cmd, args, onResult);
+		} else {
+			logError("Send to multiple target not implemented");
+		}
+	}
+
+}
+
+@:keep
+@:rtti
+class RemoteConsoleConnection {
+
+	var UID : Int = 0;
+	var parent : RemoteConsole;
+	var sock : hxd.net.Socket;
+	var waitReply : Map<Int, Dynamic->Void> = [];
+
+	public function new( parent : RemoteConsole, s : hxd.net.Socket ) {
+		this.parent = parent;
+		this.sock = s;
+		registerCommands(this);
+	}
+
+	public function close() {
 		UID = 0;
 		UID = 0;
 		waitReply = [];
 		waitReply = [];
+		if( sock != null )
+			sock.close();
+		sock = null;
+		onClose();
 	}
 	}
 
 
 	public function isConnected() {
 	public function isConnected() {
 		return sock != null;
 		return sock != null;
 	}
 	}
 
 
+	public dynamic function onClose() {
+	}
+
 	public dynamic function log( msg : String ) {
 	public dynamic function log( msg : String ) {
 		trace(msg);
 		trace(msg);
 	}
 	}
@@ -110,17 +161,11 @@ class RemoteConsole {
 	function sendData( cmd : String, args : Dynamic, id : Int ) {
 	function sendData( cmd : String, args : Dynamic, id : Int ) {
 		var obj = { cmd : cmd, args : args, id : id};
 		var obj = { cmd : cmd, args : args, id : id};
 		var bytes = haxe.io.Bytes.ofString(haxe.Json.stringify(obj) + "\n");
 		var bytes = haxe.io.Bytes.ofString(haxe.Json.stringify(obj) + "\n");
-		if( cSocks != null ) {
-			for( cs in cSocks ) {
-				cs.out.writeBytes(bytes, 0, bytes.length);
-			}
-		} else {
-			sock.out.writeBytes(bytes, 0, bytes.length);
-		}
+		sock.out.writeBytes(bytes, 0, bytes.length);
 	}
 	}
 
 
-	function handleOnData( s : hxd.net.Socket ) {
-		var str = s.input.readLine().toString();
+	public function handleOnData() {
+		var str = sock.input.readLine().toString();
 		var obj = try { haxe.Json.parse(str); } catch (e) { logError("Parse error: " + e); null; };
 		var obj = try { haxe.Json.parse(str); } catch (e) { logError("Parse error: " + e); null; };
 		if( obj == null || obj.id == null ) {
 		if( obj == null || obj.id == null ) {
 			return;
 			return;
@@ -235,6 +280,21 @@ class RemoteConsole {
 		return Sys.programPath();
 		return Sys.programPath();
 	}
 	}
 
 
+#if editor
+	@cmd function open( args : { file : String, line : Int, column : Int, cdbsheet : String } ) {
+		if( args == null )
+			return;
+		if( args.cdbsheet != null ) {
+			var sheet = hide.Ide.inst.database.getSheet(args.cdbsheet);
+			hide.Ide.inst.open("hide.view.CdbTable", {}, function(view) {
+				Std.downcast(view,hide.view.CdbTable).goto(sheet,args.line,args.column);
+			});
+		} else {
+			hide.Ide.inst.openFile(args.file);
+		}
+	}
+#end
+
 #if hl
 #if hl
 	@cmd function dump( args : { file : String } ) {
 	@cmd function dump( args : { file : String } ) {
 		hl.Gc.major();
 		hl.Gc.major();
@@ -277,5 +337,6 @@ class RemoteConsole {
 			onDone(null);
 			onDone(null);
 		}
 		}
 	}
 	}
+
 #end
 #end
 }
 }