Skip to content

Commit

Permalink
ChatConsole, lua hook adding, logged_pcall
Browse files Browse the repository at this point in the history
  • Loading branch information
justas-d committed Jan 31, 2017
1 parent 49b19f5 commit 9ea230b
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 92 deletions.
22 changes: 22 additions & 0 deletions base/Api/ApiBootstrap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@ Api.SafeParseJsonFile = function(fileDir)
return cfg
end

Api.logged_pcall = function(func, ...)
local status, result = pcall(func, ...)
if not status then Log.Warn("Logged pcall failed!:", tostring(result), debug.traceback()) end

return status, result
end


--[[ ---------------------------------------------------------------------------------------
Imported libraries
--]] ---------------------------------------------------------------------------------------
Expand All @@ -182,6 +190,20 @@ Api.json = Api.Std.require("mods/base/imports/dkjson")
--]] ---------------------------------------------------------------------------------------

Api.FunctionHook = Api.Std.require("mods/base/Api/FunctionHookHandle")
Api.ChatConsole = Api.Std.require("mods/base/Api/ChatConsole")()

--[[ ---------------------------------------------------------------------------------------
Chat commands
--]] ---------------------------------------------------------------------------------------

-- clear console
Api.ChatConsole:RegisterCommand("clear", function(...) global_chat_gui:create_ui_elements() end)

-- evaluate script
Api.ChatConsole:RegisterCommand("e", function(cmd, input) return Api.Std.loadstring(input)() end)

-- execute script file
Api.ChatConsole:RegisterCommand("f", function(cmd, input) return Api.Std.loadfile("mods/file/" .. input .. ".lua")() end)

--]] ---------------------------------------------------------------------------------------
Log.Write("Api bootstrap done.")
183 changes: 134 additions & 49 deletions base/Api/ChatConsole.lua
Original file line number Diff line number Diff line change
@@ -1,66 +1,151 @@
local function TokenizeInput(input)
local QUOTE_TOKEN = "\""
local ChatConsole = Api.class("ChatConsole")
local CommandPrefix = ";"

local function isWhiteSpace(char)
return char == ' ' or char == '\r' or char == '\n' or char == '\t'
end
function ChatConsole:initialize()
self._commands = { } -- key: command string; value: callback
self._isHijacked = false
end

--[[ ---------------------------------------------------------------------------------------
Name: SendMessage
Desc: The original send message function.
Args:
(table self) - a reference to Managers.chat
(integer channel) - internally, always called with 1. Other details unknown.
(string message) - the message to send to other clients.
--]] ---------------------------------------------------------------------------------------
ChatConsole.SendMessage = function(...) Log.Warn("Tried to call unhijacked ChatConsole.SendMessage.", debug.traceback()) end

--[[ ---------------------------------------------------------------------------------------
Name: SendLocalChat
Desc: Sends a chat message to our client.
Args:
(string message) - the message to display
(string isDev) - sets the sender color to orange for true and blue for false.
Requires the sender arg to be set.
(string isSystem) - whether to use a sender or not to.
(opt) (string sender) - the sender.
--]] ---------------------------------------------------------------------------------------
function ChatConsole.SendLocalChat(message, isDev, isSystem, sender)
assert_e(Api.IsString(message))

local msg_tables = global_chat_gui.chat_output_widget.content.message_tables

local lexData =
local msg =
{
original = input,
stream = { },
pos = 1,

matchRawChar= function(self, expected)
return stream[pos] == expected
end,

matchChar = function(self, expected)
local privatePos = pos
while isWhiteSpace(stream[privatePos]) do
privatePos = privatePos + 1
end
return stream[privatePos] == expected
end,

consumeRawChar= function(self)
self.pos = self.pos + 1
return self.stream[self.pos - 1]
end,

consumeChar = function(self)
local c
repeat c = self:consumeRawChar() until not isWhiteSpace(c)
return c
end
is_dev = isDev,
is_system = isSystem,
message = message,
sender = sender
}
message:gsub(".", function(c) table.insert(lexData.stream,c) end)

local function matchStatement(data)
local STATEMENT_TOKEN = ";"
local c = data:consumeChar()
if c ~= STATEMENT_TOKEN then error("Couldn't match statement token. Expected: " .. STATEMENT_TOKEN .. " found: " .. tostring(c)) end
end
table.insert(msg_tables, msg)
end

local function detour(message)
if message:sub(1,1) ~= CommandPrefix then return true end

local function tokenizeQuote(data)

local status, result = Api.ChatConsole:HandleChatInput(message:sub(2))

if result == nil then Log.Debug("command result is nil.") return false end

local callbackMsg
if not status then
Log.Debug("Error: " .. tostring(result))
callbackMsg = "Error: " .. tostring(result)
else
Log.Debug(tostring(result))
callbackMsg = tostring(result)
end

ChatConsole.SendLocalChat(callbackMsg, true, false, "LUA: ")

return false
end

local function tokenizeId(data)
local c = data:nextChar()
if c == QUOTE_TOKEN then return tokenizeQuote(data) end
local id = c
function ChatConsole:HijackChat()
if self._isHijacked then return end
self._isHijacked = true

self.SendMessage = Managers.chat.send_chat_message
assert_e(Api.IsFunction(Managers.chat.send_chat_message))
assert_e(Api.IsFunction(self.SendMessage))

Managers.chat.send_chat_message = function(self, channel, message, ...)
local status, result = Api.logged_pcall(detour, message)

if not status or result then Api.ChatConsole.SendMessage(Managers.chat, channel, message, ...) end
end

Log.Debug("ChatConsole: hijacked chat")
end

local function ParseTokens(tokens)
--[[ ---------------------------------------------------------------------------------------
Name: RegisterCommand
Desc: Attempts to register a chat command.
Args:
(string command) - the command trigger
(function callback) - the the callback of the command.
Signature:
(string command) - same as the command arg
(string input) - everything past command
If return value is not null, it will be locally displayed
in chat.
--]] ---------------------------------------------------------------------------------------
function ChatConsole:RegisterCommand(command, callback)
assert_e(Api.IsString(command))
assert_e(Api.IsFunction(callback))

if self._commands[command] ~= nil then return false end

self._commands[command] = callback
end

local function SubmitInput(input)

--[[ ---------------------------------------------------------------------------------------
Name: GetCommand
Args:
(string command) - the command trigger.
Return: nil or the callback of the given command.
--]] ---------------------------------------------------------------------------------------
function ChatConsole:GetCommnand(command)
assert_e(Api.IsString(command))
return self._commands[command]
end

--[[ ---------------------------------------------------------------------------------------
Name: UnregisterCommand
Args:
(string command) - the command trigger.
--]] ---------------------------------------------------------------------------------------
function ChatConsole:UnregisterCommand(command)
assert_e(Api.IsString(command))
self._commands[command] = nil
end

function RegisterCommand(command, callback)
function ChatConsole:HandleChatInput(input)
local spaceIndex = input:find(" ")

end
local cmdKey
local args

if spaceIndex == nil then
cmdKey = input
args = nil
else
cmdKey= input:sub(1, spaceIndex-1)
args = input:sub(spaceIndex+1)
end

Log.Debug("Cmd:", "\"" .. tostring(cmdKey) .. "\"", "Args:", "\"" .. tostring(args) .. "\"")

if cmdKey == nil then return false, "No command specified." end
local cmd = self._commands[cmdKey]
if cmd == nil then return false, "Command not found." end

local status, result = pcall(cmd, cmdKey, args)
if not status then return false, "Command failed: " .. tostring(result) end

return true, result
end

return ChatConsole
44 changes: 29 additions & 15 deletions base/Api/FileHookInfo.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,39 @@ local FileHookInfo = Api.class("FileHookInfo")
Desc: Creates an new immutable FileHookInfo class instance.
Args:
(string key) = the key (LoadBuffer name, or require module etc)
(string script) = directory, relative to the mod directory, to the script
(string file) = directory, relative to the mod directory, to a file script that will be executed
(string chunkString) = a string chunk of lua code that will be executed.
(table modHandle) = a handle of the mod which owns this hook.
(string scriptExecuteDir) = directory, relative to the game exe, pointing to the script
(string hookHandlerName) = name of the hook handler that manages and created this hook.
--]] ---------------------------------------------------------------------------------------
function FileHookInfo:initialize(key, script, modHandle, scriptExecuteDir, hookHandlerName)
function FileHookInfo:initialize(key, file, chunkString, modHandle, hookHandlerName)
assert_e(Api.IsString(key))
assert_e(Api.IsString(script))
assert_e(Api.IsString(chunkString) or Api.IsString(file))
assert_e(Api.IsTable(modHandle))
assert_e(Api.IsString(scriptExecuteDir))
assert_e(Api.IsString(hookHandlerName))

self._key = key
self._script = script
self._file = file
self._mod_handle = modHandle
self._script_execute_dir = scriptExecuteDir
self._hook_handler_name = hookHandlerName

if file then
self._file_execute_dir = "./mods/" .. modHandle:GetModFolder() .. "/" .. file
else
self._file_execute_dir = nil
end

if chunkString then
local _, chunk = assert_e(pcall(Api.Std.loadstring, chunkString))
self._chunk = chunk
end

getmetatable(self).__tojson = function(s, state)
return "{" .. "\"key\": \"" .. tostring(s:GetKey()) .. "\"," ..
"\"script\": \"" .. tostring(s:GetScript()) .. "\"," ..
"\"scriptExecuteDir\": \"" .. tostring(s:GetScriptExecuteDir()) .. "\"," ..
"\"file\": \"" .. tostring(s:GetFile()) .. "\"," ..
"\"chunk\": \"" .. tostring(s:GetChunk()) .. "\"," ..
"\"scriptExecuteDir\": \"" .. tostring(s:GetFileExecuteDir()) .. "\"," ..
"\"hookHandlerName\": \"" .. tostring(s:GetHookHandlerName()) .. "\"," ..
"\"modHandle_key\": \"" .. tostring(s:GetModHandle():GetKey()) .. "\"}"
end
Expand All @@ -41,11 +51,16 @@ function FileHookInfo:GetKey() return self._key end


--[[ ---------------------------------------------------------------------------------------
Name:GetScript
Name:GetFile
Returns: (string) directory, relative to the mod directory, to the hooked script
--]] ---------------------------------------------------------------------------------------
function FileHookInfo:GetScript() return self._script end
function FileHookInfo:GetFile() return self._file end

--[[ ---------------------------------------------------------------------------------------
Name: GetChunk
Returns: (string) the lua chunk to be executed.
--]] ---------------------------------------------------------------------------------------
function FileHookInfo:GetChunk() return self._chunk end

--[[ ---------------------------------------------------------------------------------------
Name: GetModHandle
Expand All @@ -55,11 +70,10 @@ function FileHookInfo:GetModHandle() return self._mod_handle end


--[[ ---------------------------------------------------------------------------------------
Name: GetScriptExecuteDir
Returns: (string) directory, relative to the game exe, pointing to the script
Name: GetFileExecuteDir
Returns: (string) directory, relative to the game exe, pointing to the file.
--]] ---------------------------------------------------------------------------------------
function FileHookInfo:GetScriptExecuteDir() return self._script_execute_dir end

function FileHookInfo:GetFileExecuteDir() return self._file_execute_dir end

--[[ ---------------------------------------------------------------------------------------
Name: GetHookHandlerName
Expand Down
2 changes: 1 addition & 1 deletion base/Internal/LuaModLoader.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
local LuaModLoader = Api.class("LuaModLoader")
local modHandleClass = Api.dofile_e("mods/base/Api/ModHandle.lua")
local modHandleClass = Api.Std.require("mods/base/Api/ModHandle")

-- owner: ModManager
function LuaModLoader:initialize(owner, ...)
Expand Down
Loading

0 comments on commit 9ea230b

Please sign in to comment.