Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions call_handlers/debugtools_commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -657,4 +657,50 @@ function Commands:set_enable_animation_text_command(session, response, bool)
radiant.util.set_global_config('enable_effect_triggers', bool)
end

function Commands:exec_script_server(session, response, script, entity)
return self:exec_script(session, response, script, entity)
end

function Commands:exec_script_client(session, response, script, entity)
return self:exec_script(session, response, script, entity)
end

function Commands:exec_script(session, response, script, entity)
local expr, error
expr, error = loadstring('return ' .. script)
if not expr then
-- Maybe it's a statement?
expr, error = loadstring(script)
end
if error then
response:reject('ERROR: ' .. error)
end

-- Set the entity as a global variable. This is terrible, but unavoidable if we want to
-- let executed code set global vars. We could instead compile the expression as a
-- function with an entity argument, but if it's a statement, it won't be able to
-- normally set global vars, so no REPL state.
local saved_entity = rawget(_G, 'e')
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like naming the variable entity is more intuitive than e. e is less verbose though 🤔

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started off with "entity", but after playing with it for a while, it was a pain to type that every time. I considered using something particularly weird like $ and replacing it with some auto-generated variable name in the script before compiling, but that seems worse,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair. Lets stick with e then

rawset(_G, 'e', entity)
local success, result = pcall(expr)
rawset(_G, 'e', saved_entity)

if success then
local output
if result == nil then
output = '<nil>'
elseif type(result) == 'string' then
output = string.gsub(string.format('%q', result), '\n', 'n')
elseif type(result) == 'table' then
output = radiant.util.table_tostring(result)
else
output = tostring(result)
end
response:resolve(output)
else
response:reject('ERROR: ' .. result)
end
return true
end

return Commands
17 changes: 14 additions & 3 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"file(/ui/entity_editor/entity_editor.html)",
"file(/ui/job_monitor/job_monitor.html)",
"file(/ui/trace_monitor/trace_monitor.html)",
"file(/ui/item_dropper/item_dropper.html)"
"file(/ui/item_dropper/item_dropper.html)",
"file(/ui/lua_console/lua_console.html)"
],
"js": [
"file(ui/debug_dock/debug_dock.js)",
Expand All @@ -38,7 +39,8 @@
"file(/ui/entity_editor/entity_editor.js)",
"file(/ui/job_monitor/job_monitor.js)",
"file(/ui/trace_monitor/trace_monitor.js)",
"file(/ui/item_dropper/item_dropper.js)"
"file(/ui/item_dropper/item_dropper.js)",
"file(/ui/lua_console/lua_console.js)"
],
"less": [
"file(/html/font-awesome-4.3.0/css/font-awesome.min.css)",
Expand All @@ -51,7 +53,8 @@
"file(/ui/entity_editor/entity_editor.less)",
"file(/ui/job_monitor/job_monitor.less)",
"file(/ui/trace_monitor/trace_monitor.less)",
"file(/ui/item_dropper/item_dropper.less)"
"file(/ui/item_dropper/item_dropper.less)",
"file(/ui/lua_console/lua_console.less)"
]
},
"functions": {
Expand Down Expand Up @@ -266,6 +269,14 @@
"set_enable_animation_text_command": {
"controller": "file(call_handlers/debugtools_commands.lua)",
"endpoint": "server"
},
"exec_script_server": {
"controller": "file(call_handlers/debugtools_commands.lua)",
"endpoint": "server"
},
"exec_script_client": {
"controller": "file(call_handlers/debugtools_commands.lua)",
"endpoint": "client"
}
},
"default_locale": "en"
Expand Down
Binary file added ui/lua_console/images/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ui/lua_console/images/resizer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ui/lua_console/images/x-2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions ui/lua_console/lua_console.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script type="text/x-handlebars" data-template-name="luaConsoleIcon">
<div id="luaConsoleIcon" title="Lua Console"></div>
</script>

<script type="text/x-handlebars" data-template-name="luaConsole">
<div id="luaConsole">
<div class="header">
Lua Console
<div id="toolbar">
<a id="serverButton" href="#" {{action "switchToServer" target="view"}} class="button selected" title="Run commands on the server">
server
</a>
<a id="clientButton" href="#" {{action "switchToClient" target="view"}} class="button" title="Run commands on the client">
client
</a>
</div>
<div id="closeConsoleButton"></div>
</div>
<div id="body">
{{view view.envContainer viewName="envContainerInstance"}}
</div>
</div>
</script>

<script type="text/x-handlebars" data-template-name="stonehearthLuaConsoleEnvironment">
<div id="stonehearthLuaConsoleEnvironment">
<div class="output">
<div id="title">[{{view.env}}] If an entity is selected, it can be accessed through the global "e" variable.</div>
</div>
<div class="input">
<input id="input" />
</div>
</div>
</script>
188 changes: 188 additions & 0 deletions ui/lua_console/lua_console.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
$(document).on('stonehearthReady', function(){
App.debugDock.addToDock(App.StonehearthLuaConsoleIcon);
radiant.call('radiant:get_config', 'mods.debugtools.enable_lua_console_hotkey')
.done(function (o) {
if (o['mods.debugtools.enable_lua_console_hotkey']) {
$(top).bind('keydown', function (e) {
if (e.keyCode == 192 && !e.originalEvent.repeat) { // Tilde/backtick
App.debugView.getView(App.StonehearthLuaConsoleIcon).$().click();
e.preventDefault();
e.stopPropagation();
}
});
}
});
});

App.StonehearthLuaConsoleIcon = App.View.extend({
templateName: 'luaConsoleIcon',
classNames: ['debugDockIcon'],

didInsertElement: function() {
$('#luaConsoleIcon').tooltipster();
this.$().click(function () {
var view = App.debugView.getView(App.StonehearthLuaConsoleView);
if (view) {
view.$().toggle();
if (view.$().is(':visible')) {
view.focus();
}
} else {
App.debugView.addView(App.StonehearthLuaConsoleView);
}
});
}
});

App.StonehearthLuaConsoleView = App.View.extend({
templateName: 'luaConsole',
uriProperty: 'model',
envContainer: Ember.ContainerView.extend(),

didInsertElement: function() {
var self = this;

this.$().draggable({ handle: '.header' });

this.$('.button').tooltipster({
theme: 'tooltipster-shdt',
arrow: true,
});

this.$("#closeConsoleButton").click(function () {
self.$().hide();
});

self.clientEnv = App.StonehearthLuaConsoleEnvironmentView.create({ env: 'client' });
self.serverEnv = App.StonehearthLuaConsoleEnvironmentView.create({ env: 'server' });

self.get('envContainerInstance').pushObject(self.serverEnv);
self.get('envContainerInstance').pushObject(self.clientEnv);

setTimeout(function () { self.switchEnv(true); }, 1);
},

switchEnv: function (isClient) {
var toShow = isClient ? this.clientEnv : this.serverEnv;
var toHide = isClient ? this.serverEnv : this.clientEnv;

toHide.$().hide();
toShow.$().show();
toShow.focus();

var toSelect = isClient ? '#clientButton' : '#serverButton';
var toDeselect = isClient ? '#serverButton' : '#clientButton';
this.$(toSelect).addClass('selected');
this.$(toDeselect).removeClass('selected');
},

focus: function () {
if (this.clientEnv.$().is(':visible')) {
this.clientEnv.focus();
} else {
this.serverEnv.focus();
}
},

actions: {
switchToServer: function () {
this.switchEnv(false);
},

switchToClient: function () {
this.switchEnv(true);
}
}
});

App.StonehearthLuaConsoleEnvironmentView = App.View.extend({
templateName: 'stonehearthLuaConsoleEnvironment',
uriProperty: 'model',
env: null,

init: function () {
this._super();
this._history = [];
this._curHistoryIdx = 0;
this._curCommand = '';
},

didInsertElement: function() {
var self = this;

self.$('#input').keydown(function(e) {
if (e.keyCode == 13 && !e.originalEvent.repeat) { // Enter
var command = $(this).val();
if (!command) return;
self._history.push(command);
$(this).val('');
self._curHistoryIdx = self._history.length - 1;

var outputArea = self.$('.output');
outputArea.append($('<div class="cmdIn">').text('> ' + command));

var resultContainer = $('<div class="cmdOut progress">').text('...');
outputArea.append(resultContainer);

var currentEntity = App.stonehearthClient.getSubSelectedEntity();
if (!currentEntity) {
currentEntity = App.stonehearthClient.getSelectedEntity();
}
radiant.call('debugtools:exec_script_' + self.env, command, currentEntity)
.done(function (o) {
resultContainer.removeClass('progress').text(o.result);
outputArea.scrollTop(outputArea[0].scrollHeight);
})
.fail(function (o) {
if (typeof o.error == 'string') {
// Client failures don't decode. We should fix it at the root, but this hack here is fine for now.
o = JSON.parse(o.error)
}
resultContainer.removeClass('progress').addClass('error').text(o.result);
outputArea.scrollTop(outputArea[0].scrollHeight);
});

outputArea.scrollTop(outputArea[0].scrollHeight);
} else if (e.keyCode == 27) { // Esc
App.debugView.getView(App.StonehearthLuaConsoleIcon).$().click();
e.preventDefault();
e.stopPropagation();
}
});

this.$('#input').keydown(function (e) {
if (e.keyCode == 38) { // UpArrow
if (self._curHistoryIdx == self._history.length - 1) {
// Remember our current command, if any.
if ($(this).val() != '') {
self._curCommand = $(this).val();
}
}

$(this).val(self._history[self._curHistoryIdx]);

self._curHistoryIdx--;
if (self._curHistoryIdx < 0) {
self._curHistoryIdx = 0;
}
e.preventDefault();
} else if (e.keyCode == 40) { // DownArrow
if (self._curHistoryIdx == self._history.length - 1) {
if (self._curCommand != '') {
$(this).val(self._curCommand);
self._curCommand = '';
}
return;
}
self._curHistoryIdx++;
$(this).val(self._history[self._curHistoryIdx]);
e.preventDefault();
}
});
},

focus: function () {
this.$('#input').focus();
},
});

Loading