Module sandboxing system
Sandboxing makes module scripts run inside an isolated lua environments, making more easier and secure to script Move and rework TextMessage using the new sandbox system
This commit is contained in:
parent
e2921c6407
commit
f289db3a9e
|
@ -16,6 +16,5 @@ Module
|
||||||
dofile 'settings'
|
dofile 'settings'
|
||||||
dofile 'keyboard'
|
dofile 'keyboard'
|
||||||
dofile 'mouse'
|
dofile 'mouse'
|
||||||
dofile 'protocol'
|
|
||||||
|
|
||||||
dofiles 'ui'
|
dofiles 'ui'
|
||||||
|
|
|
@ -51,11 +51,13 @@ function g_settings.getString(key, default)
|
||||||
end
|
end
|
||||||
|
|
||||||
function g_settings.getInteger(key, default)
|
function g_settings.getInteger(key, default)
|
||||||
return tonumber(g_settings.get(key, default))
|
local v = tonumber(g_settings.get(key, default)) or 1
|
||||||
|
return v
|
||||||
end
|
end
|
||||||
|
|
||||||
function g_settings.getNumber(key, default)
|
function g_settings.getNumber(key, default)
|
||||||
return tonumber(g_settings.get(key, default))
|
local v = tonumber(g_settings.get(key, default)) or 1
|
||||||
|
return v
|
||||||
end
|
end
|
||||||
|
|
||||||
function g_settings.getBoolean(key, default)
|
function g_settings.getBoolean(key, default)
|
||||||
|
|
|
@ -110,9 +110,34 @@ function extends(base)
|
||||||
return derived
|
return derived
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function export(what, key)
|
||||||
|
if key ~= nil then
|
||||||
|
_G[key] = what
|
||||||
|
else
|
||||||
|
for k,v in pairs(what) do
|
||||||
|
_G[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function unexport(key)
|
||||||
|
if type(key) == 'table' then
|
||||||
|
for _k,v in pairs(key) do
|
||||||
|
_G[v] = nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
_G[key] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function sandbox(what)
|
||||||
|
what = what or 2
|
||||||
|
setfenv(what, newenv())
|
||||||
|
end
|
||||||
|
|
||||||
function newenv()
|
function newenv()
|
||||||
local env = { }
|
local env = { }
|
||||||
setmetatable(env, { __index = _G} )
|
setmetatable(env, { __index = getfenv() } )
|
||||||
return env
|
return env
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -157,13 +182,6 @@ function toboolean(str)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local oldtonumber = tonumber
|
|
||||||
|
|
||||||
function tonumber(v)
|
|
||||||
if v == nil then return 0 end
|
|
||||||
return oldtonumber(v)
|
|
||||||
end
|
|
||||||
|
|
||||||
function signalcall(param, ...)
|
function signalcall(param, ...)
|
||||||
if type(param) == 'function' then
|
if type(param) == 'function' then
|
||||||
return param(...)
|
return param(...)
|
||||||
|
|
|
@ -8,7 +8,7 @@ local deathWindow
|
||||||
-- public functions
|
-- public functions
|
||||||
function PlayerDeath.init()
|
function PlayerDeath.init()
|
||||||
g_ui.importStyle('deathwindow.otui')
|
g_ui.importStyle('deathwindow.otui')
|
||||||
|
|
||||||
connect(g_game, { onDeath = PlayerDeath.display,
|
connect(g_game, { onDeath = PlayerDeath.display,
|
||||||
onGameEnd = PlayerDeath.reset })
|
onGameEnd = PlayerDeath.reset })
|
||||||
end
|
end
|
||||||
|
@ -22,7 +22,6 @@ function PlayerDeath.terminate()
|
||||||
end
|
end
|
||||||
|
|
||||||
function PlayerDeath.reset()
|
function PlayerDeath.reset()
|
||||||
GameInterface.getMapPanel():recursiveGetChildById('centerAdvance'):hide()
|
|
||||||
if deathWindow then
|
if deathWindow then
|
||||||
deathWindow:destroy()
|
deathWindow:destroy()
|
||||||
deathWindow = nil
|
deathWindow = nil
|
||||||
|
@ -37,9 +36,9 @@ end
|
||||||
function PlayerDeath.displayDeadMessage()
|
function PlayerDeath.displayDeadMessage()
|
||||||
local advanceLabel = GameInterface.getMapPanel():recursiveGetChildById('centerAdvance')
|
local advanceLabel = GameInterface.getMapPanel():recursiveGetChildById('centerAdvance')
|
||||||
if advanceLabel:isVisible() then
|
if advanceLabel:isVisible() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
TextMessage.displayEventAdvance(tr('You are dead.'))
|
TextMessage.displayEventAdvance(tr('You are dead.'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -64,7 +63,7 @@ function PlayerDeath.openWindow()
|
||||||
|
|
||||||
deathWindow.onEnter = okFunc
|
deathWindow.onEnter = okFunc
|
||||||
deathWindow.onEscape = cancelFunc
|
deathWindow.onEscape = cancelFunc
|
||||||
|
|
||||||
okButton.onClick = okFunc
|
okButton.onClick = okFunc
|
||||||
cancelButton.onClick = cancelFunc
|
cancelButton.onClick = cancelFunc
|
||||||
end
|
end
|
|
@ -0,0 +1,60 @@
|
||||||
|
function getMessageTypes(version)
|
||||||
|
if version >= 960 then
|
||||||
|
perror("TODO: message types for 9.6")
|
||||||
|
return {}
|
||||||
|
elseif version >= 861 then
|
||||||
|
return {
|
||||||
|
[13] = 'ConsoleOrange',
|
||||||
|
[14] = 'ConsoleOrange',
|
||||||
|
[15] = 'Warning',
|
||||||
|
[16] = 'EventAdvance',
|
||||||
|
[17] = 'EventDefault',
|
||||||
|
[18] = 'StatusDefault',
|
||||||
|
[19] = 'Info',
|
||||||
|
[20] = 'StatusSmall',
|
||||||
|
[21] = 'ConsoleBlue',
|
||||||
|
[22] = 'ConsoleRed'
|
||||||
|
}
|
||||||
|
elseif version >= 854 then
|
||||||
|
return {
|
||||||
|
[18] = 'ConsoleRed',
|
||||||
|
[19] = 'ConsoleOrange',
|
||||||
|
[20] = 'ConsoleOrange',
|
||||||
|
[21] = 'Warning',
|
||||||
|
[22] = 'EventAdvance',
|
||||||
|
[23] = 'EventDefault',
|
||||||
|
[24] = 'StatusDefault',
|
||||||
|
[25] = 'Info',
|
||||||
|
[26] = 'StatusSmall',
|
||||||
|
[27] = 'ConsoleBlue'
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
[18] = 'Warning',
|
||||||
|
[19] = 'EventAdvance',
|
||||||
|
[20] = 'EventDefault',
|
||||||
|
[21] = 'StatusDefault',
|
||||||
|
[22] = 'Info',
|
||||||
|
[23] = 'StatusSmall',
|
||||||
|
[24] = 'ConsoleBlue',
|
||||||
|
[25] = 'ConsoleRed',
|
||||||
|
[26] = 'ConsoleOrange',
|
||||||
|
[27] = 'ConsoleOrange',
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function parseTextMessage(msg)
|
||||||
|
local msgtype = msg:getU8()
|
||||||
|
local text = msg:getString()
|
||||||
|
msgtype = getMessageTypes(g_game.getProtocolVersion())[msgtype]
|
||||||
|
signalcall(g_game.onTextMessage, msgtype, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function registerProtocol()
|
||||||
|
ProtocolGame.registerOpcode(GameServerOpcodes.GameServerTextMessage, parseTextMessage)
|
||||||
|
end
|
||||||
|
|
||||||
|
function unregisterProtocol()
|
||||||
|
ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerTextMessage)
|
||||||
|
end
|
|
@ -1,41 +1,104 @@
|
||||||
TextMessage = {}
|
MessageTypesConf = {
|
||||||
|
ConsoleRed = { color = '#F55E5E', consoleTab = tr('Default') },
|
||||||
-- require styles
|
ConsoleOrange = { color = '#FE6500', consoleTab = tr('Default') },
|
||||||
g_ui.importStyle('textmessage.otui')
|
ConsoleBlue = { color = '#9F9DFD', consoleTab = tr('Default') },
|
||||||
|
Warning = { color = '#F55E5E', consoleTab = tr('Server Log'), labelId = 'warningLabel' },
|
||||||
-- private variables
|
Info = { color = '#00EB00', consoleTab = tr('Server Log'), labelId = 'infoLabel', consoleOption = 'showInfoMessagesInConsole' },
|
||||||
local MessageTypes = {
|
EventAdvance = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'advanceLabel', consoleOption = 'showEventMessagesInConsole' },
|
||||||
consoleRed = { color = '#F55E5E', consoleTab = tr('Default') },
|
EventDefault = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'statusLabel', consoleOption = 'showEventMessagesInConsole' },
|
||||||
consoleOrange = { color = '#FE6500', consoleTab = tr('Default') },
|
StatusDefault = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'statusLabel', consoleOption = 'showStatusMessagesInConsole' },
|
||||||
consoleBlue = { color = '#9F9DFD', consoleTab = tr('Default') },
|
StatusSmall = { color = '#FFFFFF', labelId = 'statusLabel' },
|
||||||
warning = { color = '#F55E5E', consoleTab = tr('Server Log'), labelId = 'centerWarning' },
|
Private = { color = '#5FF7F7', labelId = 'privateLabel' }
|
||||||
infoDescription = { color = '#00EB00', consoleTab = tr('Server Log'), labelId = 'centerInfo', consoleOption = 'showInfoMessagesInConsole' },
|
|
||||||
eventAdvance = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'centerAdvance', consoleOption = 'showEventMessagesInConsole' },
|
|
||||||
eventDefault = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'bottomStatus', consoleOption = 'showEventMessagesInConsole' },
|
|
||||||
statusDefault = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'bottomStatus', consoleOption = 'showStatusMessagesInConsole' },
|
|
||||||
statusSmall = { color = '#FFFFFF', labelId = 'bottomStatus' },
|
|
||||||
private = { color = '#5FF7F7', labelId = 'centerPrivate' }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local centerTextMessagePanel
|
centerTextMessagePanel = nil
|
||||||
local bottomStatusLabel
|
statusLabel = nil
|
||||||
local privateLabel
|
privateLabel = nil
|
||||||
|
warningLabel = nil
|
||||||
|
advanceLabel = nil
|
||||||
|
infoLabel = nil
|
||||||
|
|
||||||
-- private functions
|
function init()
|
||||||
local function displayMessage(msgtype, msg, time)
|
connect(g_game, {
|
||||||
|
onTextMessage = displayMessage,
|
||||||
|
onGameStart = clearMessages
|
||||||
|
})
|
||||||
|
registerProtocol()
|
||||||
|
|
||||||
|
g_ui.importStyle('textmessage.otui')
|
||||||
|
centerTextMessagePanel = g_ui.createWidget('Panel', GameInterface.getMapPanel())
|
||||||
|
centerTextMessagePanel:setId('centerTextMessagePanel')
|
||||||
|
|
||||||
|
local layout = UIVerticalLayout.create(centerTextMessagePanel)
|
||||||
|
layout:setFitChildren(true)
|
||||||
|
centerTextMessagePanel:setLayout(layout)
|
||||||
|
centerTextMessagePanel:setWidth(360)
|
||||||
|
centerTextMessagePanel:centerIn('parent')
|
||||||
|
|
||||||
|
warningLabel = createTextMessageLabel('warningLabel', centerTextMessagePanel, 'CenterLabel')
|
||||||
|
advanceLabel = createTextMessageLabel('advanceLabel', centerTextMessagePanel, 'CenterLabel')
|
||||||
|
infoLabel = createTextMessageLabel('infoLabel', centerTextMessagePanel, 'CenterLabel')
|
||||||
|
privateLabel = createTextMessageLabel('privateLabel', GameInterface.getMapPanel(), 'TopCenterLabel')
|
||||||
|
statusLabel = createTextMessageLabel('statusLabel', GameInterface.getMapPanel(), 'BottomLabel')
|
||||||
|
|
||||||
|
export({
|
||||||
|
clearMessages = clearMessages,
|
||||||
|
displayStatus = function() displayMessage(MessageTypes.StatusSmall, msg) end,
|
||||||
|
displayEventAdvance = function() displayMessage(MessageTypes.EventAdvance, msg, time) end,
|
||||||
|
displayPrivate = function() displayPrivate(msg, time) end
|
||||||
|
}, 'TextMessage')
|
||||||
|
end
|
||||||
|
|
||||||
|
function terminate()
|
||||||
|
disconnect(g_game, {
|
||||||
|
onTextMessage = display,
|
||||||
|
onGameStart = clearMessages
|
||||||
|
})
|
||||||
|
unregisterProtocol()
|
||||||
|
|
||||||
|
removeEvent(warningLabel.hideEvent)
|
||||||
|
removeEvent(advanceLabel.hideEvent)
|
||||||
|
removeEvent(infoLabel.hideEvent)
|
||||||
|
removeEvent(privateLabel.hideEvent)
|
||||||
|
removeEvent(statusLabel.hideEvent)
|
||||||
|
|
||||||
|
centerTextMessagePanel:destroy()
|
||||||
|
statusLabel:destroy()
|
||||||
|
privateLabel:destroy()
|
||||||
|
|
||||||
|
unexport('TextMessage')
|
||||||
|
end
|
||||||
|
|
||||||
|
function clearMessages()
|
||||||
|
warningLabel:hide()
|
||||||
|
advanceLabel:hide()
|
||||||
|
infoLabel:hide()
|
||||||
|
privateLabel:hide()
|
||||||
|
statusLabel:hide()
|
||||||
|
end
|
||||||
|
|
||||||
|
function createTextMessageLabel(id, parent, class)
|
||||||
|
local label = g_ui.createWidget(class, parent)
|
||||||
|
label:setFont('verdana-11px-rounded')
|
||||||
|
label:setId(id)
|
||||||
|
return label
|
||||||
|
end
|
||||||
|
|
||||||
|
function displayMessage(msgtype, msg, time)
|
||||||
if not g_game.isOnline() then return end
|
if not g_game.isOnline() then return end
|
||||||
|
msgtypeconf = MessageTypesConf[msgtype]
|
||||||
|
|
||||||
if msgtype.consoleTab ~= nil then
|
if msgtypeconf.consoleTab ~= nil then
|
||||||
if msgtype.consoleOption == nil or Options.getOption(msgtype.consoleOption) then
|
if msgtypeconf.consoleOption == nil or Options.getOption(msgtypeconf.consoleOption) then
|
||||||
Console.addText(msg, msgtype, msgtype.consoleTab)
|
Console.addText(msg, msgtypeconf, msgtypeconf.consoleTab)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if msgtype.labelId then
|
if msgtypeconf.labelId then
|
||||||
local label = GameInterface.getMapPanel():recursiveGetChildById(msgtype.labelId)
|
local label = GameInterface.getMapPanel():recursiveGetChildById(msgtypeconf.labelId)
|
||||||
|
|
||||||
label:setText(msg)
|
label:setText(msg)
|
||||||
label:setColor(msgtype.color)
|
label:setColor(msgtypeconf.color)
|
||||||
|
|
||||||
if not time then
|
if not time then
|
||||||
time = math.max(#msg * 100, 4000)
|
time = math.max(#msg * 100, 4000)
|
||||||
|
@ -47,77 +110,3 @@ local function displayMessage(msgtype, msg, time)
|
||||||
label.hideEvent = scheduleEvent(function() label:setVisible(false) end, time)
|
label.hideEvent = scheduleEvent(function() label:setVisible(false) end, time)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function createTextMessageLabel(id, parent, class)
|
|
||||||
local label = g_ui.createWidget(class, parent)
|
|
||||||
label:setFont('verdana-11px-rounded')
|
|
||||||
label:setId(id)
|
|
||||||
return label
|
|
||||||
end
|
|
||||||
|
|
||||||
-- public functions
|
|
||||||
function TextMessage.init()
|
|
||||||
connect(g_game, { onTextMessage = TextMessage.display,
|
|
||||||
onGameStart = TextMessage.clearMessages })
|
|
||||||
|
|
||||||
centerTextMessagePanel = g_ui.createWidget('Panel', GameInterface.getMapPanel())
|
|
||||||
centerTextMessagePanel:setId('centerTextMessagePanel')
|
|
||||||
|
|
||||||
local layout = UIVerticalLayout.create(centerTextMessagePanel)
|
|
||||||
layout:setFitChildren(true)
|
|
||||||
centerTextMessagePanel:setLayout(layout)
|
|
||||||
centerTextMessagePanel:setWidth(360)
|
|
||||||
centerTextMessagePanel:centerIn('parent')
|
|
||||||
|
|
||||||
createTextMessageLabel('centerWarning', centerTextMessagePanel, 'CenterLabel')
|
|
||||||
createTextMessageLabel('centerAdvance', centerTextMessagePanel, 'CenterLabel')
|
|
||||||
createTextMessageLabel('centerInfo', centerTextMessagePanel, 'CenterLabel')
|
|
||||||
|
|
||||||
privateLabel = createTextMessageLabel('centerPrivate', GameInterface.getMapPanel(), 'TopCenterLabel')
|
|
||||||
bottomStatusLabel = createTextMessageLabel('bottomStatus', GameInterface.getMapPanel(), 'BottomLabel')
|
|
||||||
end
|
|
||||||
|
|
||||||
function TextMessage.terminate()
|
|
||||||
disconnect(g_game, { onDeath = TextMessage.displayDeadMessage,
|
|
||||||
onTextMessage = TextMessage.display,
|
|
||||||
onGameStart = TextMessage.clearMessages })
|
|
||||||
removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerWarning').hideEvent)
|
|
||||||
removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerAdvance').hideEvent)
|
|
||||||
removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerInfo').hideEvent)
|
|
||||||
removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerPrivate').hideEvent)
|
|
||||||
removeEvent(GameInterface.getMapPanel():recursiveGetChildById('bottomStatus').hideEvent)
|
|
||||||
centerTextMessagePanel:destroy()
|
|
||||||
bottomStatusLabel:destroy()
|
|
||||||
privateLabel:destroy()
|
|
||||||
centerTextMessagePanel = nil
|
|
||||||
bottomStatusLabel = nil
|
|
||||||
privateLabel = nil
|
|
||||||
TextMessage = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function TextMessage.clearMessages()
|
|
||||||
GameInterface.getMapPanel():recursiveGetChildById('centerWarning'):hide()
|
|
||||||
GameInterface.getMapPanel():recursiveGetChildById('centerAdvance'):hide()
|
|
||||||
GameInterface.getMapPanel():recursiveGetChildById('centerInfo'):hide()
|
|
||||||
GameInterface.getMapPanel():recursiveGetChildById('centerPrivate'):hide()
|
|
||||||
GameInterface.getMapPanel():recursiveGetChildById('bottomStatus'):hide()
|
|
||||||
end
|
|
||||||
|
|
||||||
function TextMessage.displayStatus(msg, time)
|
|
||||||
displayMessage(MessageTypes.statusSmall, msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
function TextMessage.displayEventAdvance(msg, time)
|
|
||||||
displayMessage(MessageTypes.eventAdvance, msg, time)
|
|
||||||
end
|
|
||||||
|
|
||||||
function TextMessage.displayPrivate(msg, time)
|
|
||||||
displayMessage(MessageTypes.private, msg, time)
|
|
||||||
end
|
|
||||||
|
|
||||||
function TextMessage.display(msgtypedesc, msg)
|
|
||||||
local msgtype = MessageTypes[msgtypedesc]
|
|
||||||
if msgtype then
|
|
||||||
displayMessage(msgtype, msg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -3,13 +3,14 @@ Module
|
||||||
description: Manage game text messages
|
description: Manage game text messages
|
||||||
author: edubart
|
author: edubart
|
||||||
website: www.otclient.info
|
website: www.otclient.info
|
||||||
|
sandboxed: true
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
- game_interface
|
- game_interface
|
||||||
|
|
||||||
@onLoad: |
|
scripts:
|
||||||
dofile 'textmessage'
|
- protocol.lua
|
||||||
TextMessage.init()
|
- textmessage.lua
|
||||||
|
|
||||||
@onUnload: |
|
@onLoad: init()
|
||||||
TextMessage.terminate()
|
@onUnload: terminate()
|
||||||
|
|
|
@ -10,7 +10,7 @@ Module
|
||||||
|
|
||||||
@onLoad: |
|
@onLoad: |
|
||||||
dofile 'const'
|
dofile 'const'
|
||||||
|
dofile 'protocol'
|
||||||
dofile 'protocollogin'
|
dofile 'protocollogin'
|
||||||
dofile 'protocolgame'
|
dofile 'protocolgame'
|
||||||
dofile 'game'
|
dofile 'game'
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "modulemanager.h"
|
#include "modulemanager.h"
|
||||||
|
#include "resourcemanager.h"
|
||||||
|
|
||||||
#include <framework/otml/otml.h>
|
#include <framework/otml/otml.h>
|
||||||
#include <framework/luaengine/luainterface.h>
|
#include <framework/luaengine/luainterface.h>
|
||||||
|
@ -29,6 +30,8 @@
|
||||||
Module::Module(const std::string& name)
|
Module::Module(const std::string& name)
|
||||||
{
|
{
|
||||||
m_name = name;
|
m_name = name;
|
||||||
|
g_lua.newEnvironment();
|
||||||
|
m_sandboxEnv = g_lua.ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Module::load()
|
bool Module::load()
|
||||||
|
@ -36,24 +39,43 @@ bool Module::load()
|
||||||
if(m_loaded)
|
if(m_loaded)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for(const std::string& depName : m_dependencies) {
|
try {
|
||||||
ModulePtr dep = g_modules.getModule(depName);
|
for(const std::string& depName : m_dependencies) {
|
||||||
if(!dep) {
|
ModulePtr dep = g_modules.getModule(depName);
|
||||||
g_logger.error(stdext::format("Unable to load module '%s' because dependency '%s' was not found", m_name, depName));
|
if(!dep)
|
||||||
return false;
|
stdext::throw_exception(stdext::format("dependency '%s' was not found", m_name, depName));
|
||||||
|
|
||||||
|
if(!dep->isLoaded() && !dep->load())
|
||||||
|
stdext::throw_exception(stdext::format("dependency '%s' has failed to load", m_name, depName));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!dep->isLoaded() && !dep->load()) {
|
for(const std::string& script : m_scripts) {
|
||||||
g_logger.error(stdext::format("Unable to load module '%s' because dependency '%s' has failed to load", m_name, depName));
|
g_lua.loadScript(script);
|
||||||
return false;
|
if(m_sandboxed) {
|
||||||
|
g_lua.getRef(m_sandboxEnv);
|
||||||
|
g_lua.setEnv();
|
||||||
|
}
|
||||||
|
g_lua.safeCall(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& onLoadBuffer = std::get<0>(m_onLoadFunc);
|
||||||
|
const std::string& onLoadSource = std::get<1>(m_onLoadFunc);
|
||||||
|
if(!onLoadBuffer.empty()) {
|
||||||
|
g_lua.loadBuffer(onLoadBuffer, onLoadSource);
|
||||||
|
if(m_sandboxed) {
|
||||||
|
g_lua.getRef(m_sandboxEnv);
|
||||||
|
g_lua.setEnv();
|
||||||
|
}
|
||||||
|
g_lua.safeCall(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_logger.debug(stdext::format("Loaded module '%s'", m_name));
|
||||||
|
} catch(stdext::exception& e) {
|
||||||
|
g_logger.error(stdext::format("Unable to load module '%s': %s", m_name, e.what()));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_loadCallback)
|
|
||||||
m_loadCallback();
|
|
||||||
|
|
||||||
m_loaded = true;
|
m_loaded = true;
|
||||||
g_logger.debug(stdext::format("Loaded module '%s'", m_name));
|
|
||||||
g_modules.updateModuleLoadOrder(asModule());
|
g_modules.updateModuleLoadOrder(asModule());
|
||||||
|
|
||||||
for(const std::string& modName : m_loadLaterModules) {
|
for(const std::string& modName : m_loadLaterModules) {
|
||||||
|
@ -70,8 +92,21 @@ bool Module::load()
|
||||||
void Module::unload()
|
void Module::unload()
|
||||||
{
|
{
|
||||||
if(m_loaded) {
|
if(m_loaded) {
|
||||||
if(m_unloadCallback)
|
try {
|
||||||
m_unloadCallback();
|
const std::string& onUnloadBuffer = std::get<0>(m_onUnloadFunc);
|
||||||
|
const std::string& onUnloadSource = std::get<1>(m_onUnloadFunc);
|
||||||
|
if(!onUnloadBuffer.empty()) {
|
||||||
|
g_lua.loadBuffer(onUnloadBuffer, onUnloadSource);
|
||||||
|
if(m_sandboxed) {
|
||||||
|
g_lua.getRef(m_sandboxEnv);
|
||||||
|
g_lua.setEnv();
|
||||||
|
}
|
||||||
|
g_lua.safeCall(0, 0);
|
||||||
|
}
|
||||||
|
} catch(stdext::exception& e) {
|
||||||
|
g_logger.error(stdext::format("Unable to unload module '%s': %s", m_name, e.what()));
|
||||||
|
}
|
||||||
|
|
||||||
m_loaded = false;
|
m_loaded = false;
|
||||||
//g_logger.info(stdext::format("Unloaded module '%s'", m_name));
|
//g_logger.info(stdext::format("Unloaded module '%s'", m_name));
|
||||||
g_modules.updateModuleLoadOrder(asModule());
|
g_modules.updateModuleLoadOrder(asModule());
|
||||||
|
@ -109,6 +144,7 @@ void Module::discover(const OTMLNodePtr& moduleNode)
|
||||||
m_version = moduleNode->valueAt("version", none);
|
m_version = moduleNode->valueAt("version", none);
|
||||||
m_autoLoad = moduleNode->valueAt<bool>("autoload", false);
|
m_autoLoad = moduleNode->valueAt<bool>("autoload", false);
|
||||||
m_reloadable = moduleNode->valueAt<bool>("reloadable", true);
|
m_reloadable = moduleNode->valueAt<bool>("reloadable", true);
|
||||||
|
m_sandboxed = moduleNode->valueAt<bool>("sandboxed", false);
|
||||||
m_autoLoadPriority = moduleNode->valueAt<int>("autoload-priority", 9999);
|
m_autoLoadPriority = moduleNode->valueAt<int>("autoload-priority", 9999);
|
||||||
|
|
||||||
if(OTMLNodePtr node = moduleNode->get("dependencies")) {
|
if(OTMLNodePtr node = moduleNode->get("dependencies")) {
|
||||||
|
@ -116,22 +152,19 @@ void Module::discover(const OTMLNodePtr& moduleNode)
|
||||||
m_dependencies.push_back(tmp->value());
|
m_dependencies.push_back(tmp->value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// set onLoad callback
|
if(OTMLNodePtr node = moduleNode->get("scripts")) {
|
||||||
if(OTMLNodePtr node = moduleNode->get("@onLoad")) {
|
for(const OTMLNodePtr& tmp : node->children())
|
||||||
g_lua.loadFunction(node->value(), "@" + node->source() + "[" + node->tag() + "]");
|
m_scripts.push_back(stdext::resolve_path(tmp->value(), node->source()));
|
||||||
g_lua.useValue();
|
|
||||||
m_loadCallback = g_lua.polymorphicPop<std::function<void()>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// set onUnload callback
|
|
||||||
if(OTMLNodePtr node = moduleNode->get("@onUnload")) {
|
|
||||||
g_lua.loadFunction(node->value(), "@" + node->source() + "[" + node->tag() + "]");
|
|
||||||
g_lua.useValue();
|
|
||||||
m_unloadCallback = g_lua.polymorphicPop<std::function<void()>>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(OTMLNodePtr node = moduleNode->get("load-later")) {
|
if(OTMLNodePtr node = moduleNode->get("load-later")) {
|
||||||
for(const OTMLNodePtr& tmp : node->children())
|
for(const OTMLNodePtr& tmp : node->children())
|
||||||
m_loadLaterModules.push_back(tmp->value());
|
m_loadLaterModules.push_back(tmp->value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(OTMLNodePtr node = moduleNode->get("@onLoad"))
|
||||||
|
m_onLoadFunc = std::make_tuple(node->value(), "@" + node->source() + "[" + node->tag() + "]");
|
||||||
|
|
||||||
|
if(OTMLNodePtr node = moduleNode->get("@onUnload"))
|
||||||
|
m_onUnloadFunc = std::make_tuple(node->value(), "@" + node->source() + "[" + node->tag() + "]");
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,11 @@ private:
|
||||||
Boolean<false> m_loaded;
|
Boolean<false> m_loaded;
|
||||||
Boolean<false> m_autoLoad;
|
Boolean<false> m_autoLoad;
|
||||||
Boolean<false> m_reloadable;
|
Boolean<false> m_reloadable;
|
||||||
|
Boolean<false> m_sandboxed;
|
||||||
int m_autoLoadPriority;
|
int m_autoLoadPriority;
|
||||||
|
int m_sandboxEnv;
|
||||||
|
std::tuple<std::string, std::string> m_onLoadFunc;
|
||||||
|
std::tuple<std::string, std::string> m_onUnloadFunc;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::string m_description;
|
std::string m_description;
|
||||||
std::string m_author;
|
std::string m_author;
|
||||||
|
@ -73,6 +77,7 @@ private:
|
||||||
std::function<void()> m_loadCallback;
|
std::function<void()> m_loadCallback;
|
||||||
std::function<void()> m_unloadCallback;
|
std::function<void()> m_unloadCallback;
|
||||||
std::list<std::string> m_dependencies;
|
std::list<std::string> m_dependencies;
|
||||||
|
std::list<std::string> m_scripts;
|
||||||
std::list<std::string> m_loadLaterModules;
|
std::list<std::string> m_loadLaterModules;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -306,13 +306,13 @@ bool LuaInterface::safeRunScript(const std::string& fileName)
|
||||||
void LuaInterface::runScript(const std::string& fileName)
|
void LuaInterface::runScript(const std::string& fileName)
|
||||||
{
|
{
|
||||||
loadScript(fileName);
|
loadScript(fileName);
|
||||||
safeCall();
|
safeCall(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaInterface::runBuffer(const std::string& buffer, const std::string& source)
|
void LuaInterface::runBuffer(const std::string& buffer, const std::string& source)
|
||||||
{
|
{
|
||||||
loadBuffer(buffer, source);
|
loadBuffer(buffer, source);
|
||||||
safeCall();
|
safeCall(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaInterface::loadScript(const std::string& fileName)
|
void LuaInterface::loadScript(const std::string& fileName)
|
||||||
|
@ -425,7 +425,7 @@ std::string LuaInterface::getCurrentSourcePath(int level)
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LuaInterface::safeCall(int numArgs)
|
int LuaInterface::safeCall(int numArgs, int numRets)
|
||||||
{
|
{
|
||||||
assert(hasIndex(-numArgs-1));
|
assert(hasIndex(-numArgs-1));
|
||||||
|
|
||||||
|
@ -446,22 +446,33 @@ int LuaInterface::safeCall(int numArgs)
|
||||||
if(ret != 0)
|
if(ret != 0)
|
||||||
throw LuaException(popString());
|
throw LuaException(popString());
|
||||||
|
|
||||||
|
int rets = (stackSize() + numArgs + 1) - previousStackSize;
|
||||||
|
while(numRets != -1 && rets != numRets) {
|
||||||
|
if(rets < numRets) {
|
||||||
|
pushNil();
|
||||||
|
rets++;
|
||||||
|
} else {
|
||||||
|
pop();
|
||||||
|
rets--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// returns the number of results
|
// returns the number of results
|
||||||
return (stackSize() + numArgs + 1) - previousStackSize;
|
return rets;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LuaInterface::signalCall(int numArgs, int requestedResults)
|
int LuaInterface::signalCall(int numArgs, int numRets)
|
||||||
{
|
{
|
||||||
int numRets = 0;
|
int rets = 0;
|
||||||
int funcIndex = -numArgs-1;
|
int funcIndex = -numArgs-1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// must be a function
|
// must be a function
|
||||||
if(isFunction(funcIndex)) {
|
if(isFunction(funcIndex)) {
|
||||||
numRets = safeCall(numArgs);
|
rets = safeCall(numArgs);
|
||||||
|
|
||||||
if(requestedResults != -1) {
|
if(numRets != -1) {
|
||||||
if(numRets != requestedResults)
|
if(rets != numRets)
|
||||||
throw LuaException("function call didn't return the expected number of results", 0);
|
throw LuaException("function call didn't return the expected number of results", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,8 +502,8 @@ int LuaInterface::signalCall(int numArgs, int requestedResults)
|
||||||
}
|
}
|
||||||
pop(numArgs + 1); // pops the table of function and arguments
|
pop(numArgs + 1); // pops the table of function and arguments
|
||||||
|
|
||||||
if(requestedResults == 1) {
|
if(numRets == 1) {
|
||||||
numRets = 1;
|
rets = 1;
|
||||||
pushBoolean(done);
|
pushBoolean(done);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -509,13 +520,13 @@ int LuaInterface::signalCall(int numArgs, int requestedResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushes nil values if needed
|
// pushes nil values if needed
|
||||||
while(requestedResults != -1 && numRets < requestedResults) {
|
while(numRets != -1 && rets < numRets) {
|
||||||
pushNil();
|
pushNil();
|
||||||
numRets++;
|
rets++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the number of results on the stack
|
// returns the number of results on the stack
|
||||||
return numRets;
|
return rets;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaInterface::newEnvironment()
|
void LuaInterface::newEnvironment()
|
||||||
|
|
|
@ -174,13 +174,13 @@ public:
|
||||||
/// results are pushed onto the stack.
|
/// results are pushed onto the stack.
|
||||||
/// @exception LuaException is thrown on any lua error
|
/// @exception LuaException is thrown on any lua error
|
||||||
/// @return number of results
|
/// @return number of results
|
||||||
int safeCall(int numArgs = 0);
|
int safeCall(int numArgs = 0, int numRets = -1);
|
||||||
|
|
||||||
/// Same as safeCall but catches exceptions and can also calls a table of functions,
|
/// Same as safeCall but catches exceptions and can also calls a table of functions,
|
||||||
/// if any error occurs it will be reported to stdout and returns 0 results
|
/// if any error occurs it will be reported to stdout and returns 0 results
|
||||||
/// @param requestedResults is the number of results requested to pushes onto the stack,
|
/// @param requestedResults is the number of results requested to pushes onto the stack,
|
||||||
/// if supplied, the call will always pushes that number of results, even if it fails
|
/// if supplied, the call will always pushes that number of results, even if it fails
|
||||||
int signalCall(int numArgs = 0, int requestedResults = -1);
|
int signalCall(int numArgs = 0, int numRets = -1);
|
||||||
|
|
||||||
/// @brief Creates a new environment table
|
/// @brief Creates a new environment table
|
||||||
/// The new environment table is redirected to the global environment (aka _G),
|
/// The new environment table is redirected to the global environment (aka _G),
|
||||||
|
|
|
@ -60,7 +60,7 @@ protected:
|
||||||
void processWalkCancel(Otc::Direction direction);
|
void processWalkCancel(Otc::Direction direction);
|
||||||
|
|
||||||
// message related
|
// message related
|
||||||
void processTextMessage(const std::string& type, const std::string& message);
|
void processTextMessage(const std::string& type, const std::string& message); // deprecated
|
||||||
void processCreatureSpeak(const std::string& name, int level, Otc::SpeakType type, const std::string& message, int channelId, const Position& creaturePos);
|
void processCreatureSpeak(const std::string& name, int level, Otc::SpeakType type, const std::string& message, int channelId, const Position& creaturePos);
|
||||||
|
|
||||||
// container related
|
// container related
|
||||||
|
|
|
@ -556,7 +556,6 @@ void Map::clean()
|
||||||
m_towns.clear();
|
m_towns.clear();
|
||||||
m_houses.clear();
|
m_houses.clear();
|
||||||
m_creatures.clear();
|
m_creatures.clear();
|
||||||
m_monsters.clear();
|
|
||||||
m_tilesRect = Rect(65534, 65534, 0, 0);
|
m_tilesRect = Rect(65534, 65534, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -329,77 +329,6 @@ namespace Proto {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MessageTypes {
|
|
||||||
#if PROTOCOL>=910
|
|
||||||
// 1-3
|
|
||||||
MessageConsoleBlue = 4, // old
|
|
||||||
// 5-11
|
|
||||||
MessageConsoleRed = 12, // old
|
|
||||||
// 13-15
|
|
||||||
MessageStatusDefault = 16, // old
|
|
||||||
MessageWarning, // old
|
|
||||||
MessageEventAdvance, // old
|
|
||||||
MessageStatusSmall, // old
|
|
||||||
MessageInfoDescription, // old
|
|
||||||
MessageDamageDealt, // new
|
|
||||||
MessageDamageReceived, // new
|
|
||||||
MessageHealed, // new
|
|
||||||
MessageExperience, // new
|
|
||||||
MessageDamageOthers, // new
|
|
||||||
MessageHealedOthers, // new
|
|
||||||
MessageExperienceOthers, // new
|
|
||||||
MessageEventDefault, // old
|
|
||||||
MessageLoot, // new
|
|
||||||
MessageTradeNpc, // unused
|
|
||||||
MessageChannelGuild, // new
|
|
||||||
MessagePartyManagment, // unused
|
|
||||||
MessageParty, // unused
|
|
||||||
MessageEventOrange, // old
|
|
||||||
MessageConsoleOrange, // old
|
|
||||||
MessageReport, // unused
|
|
||||||
MessageHotkeyUse, // unused
|
|
||||||
MessageTutorialHint, // unused
|
|
||||||
|
|
||||||
// unsupported
|
|
||||||
MessageConsoleOrange2 = 255
|
|
||||||
#elif PROTOCOL>=861
|
|
||||||
MessageConsoleOrange = 13,
|
|
||||||
MessageConsoleOrange2,
|
|
||||||
MessageWarning,
|
|
||||||
MessageEventAdvance,
|
|
||||||
MessageEventDefault,
|
|
||||||
MessageStatusDefault,
|
|
||||||
MessageInfoDescription,
|
|
||||||
MessageStatusSmall,
|
|
||||||
MessageConsoleBlue,
|
|
||||||
MessageConsoleRed
|
|
||||||
#elif PROTOCOL>=854
|
|
||||||
MessageConsoleRed = 18,
|
|
||||||
MessageConsoleOrange,
|
|
||||||
MessageConsoleOrange2,
|
|
||||||
MessageWarning,
|
|
||||||
MessageEventAdvance,
|
|
||||||
MessageEventDefault,
|
|
||||||
MessageStatusDefault,
|
|
||||||
MessageInfoDescription,
|
|
||||||
MessageStatusSmall,
|
|
||||||
MessageConsoleBlue
|
|
||||||
#elif PROTOCOL>=810
|
|
||||||
MessageWarning = 18,
|
|
||||||
MessageEventAdvance,
|
|
||||||
MessageEventDefault,
|
|
||||||
MessageStatusDefault,
|
|
||||||
MessageInfoDescription,
|
|
||||||
MessageStatusSmall,
|
|
||||||
MessageConsoleBlue,
|
|
||||||
MessageConsoleRed,
|
|
||||||
|
|
||||||
// unsupported
|
|
||||||
MessageConsoleOrange = 255,
|
|
||||||
MessageConsoleOrange2,
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
enum CreatureType {
|
enum CreatureType {
|
||||||
CreatureTypePlayer = 0,
|
CreatureTypePlayer = 0,
|
||||||
CreatureTypeMonster,
|
CreatureTypeMonster,
|
||||||
|
@ -459,24 +388,6 @@ namespace Proto {
|
||||||
return Proto::ServerSpeakSay;
|
return Proto::ServerSpeakSay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string translateTextMessageType(int type) {
|
|
||||||
switch(type) {
|
|
||||||
case Proto::MessageConsoleOrange: return "consoleOrange";
|
|
||||||
case Proto::MessageConsoleOrange2: return "consoleOrange";
|
|
||||||
case Proto::MessageWarning: return "warning";
|
|
||||||
case Proto::MessageEventAdvance: return "eventAdvance";
|
|
||||||
case Proto::MessageEventDefault: return "eventDefault";
|
|
||||||
case Proto::MessageStatusDefault: return "statusDefault";
|
|
||||||
case Proto::MessageInfoDescription: return "infoDescription";
|
|
||||||
case Proto::MessageStatusSmall: return "statusSmall";
|
|
||||||
case Proto::MessageConsoleBlue: return "consoleBlue";
|
|
||||||
case Proto::MessageConsoleRed: return "consoleRed";
|
|
||||||
default:
|
|
||||||
g_logger.error(stdext::format("unknown protocol text message type %d", type));
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1067,12 +1067,9 @@ void ProtocolGame::parseRuleViolationLock(const InputMessagePtr& msg)
|
||||||
|
|
||||||
void ProtocolGame::parseTextMessage(const InputMessagePtr& msg)
|
void ProtocolGame::parseTextMessage(const InputMessagePtr& msg)
|
||||||
{
|
{
|
||||||
int type = msg->getU8();
|
msg->getU8(); // type
|
||||||
|
msg->getString(); // message
|
||||||
std::string typeDesc = Proto::translateTextMessageType(type);
|
// this is now handled by game_textmessage module
|
||||||
std::string message = msg->getString();
|
|
||||||
|
|
||||||
g_game.processTextMessage(typeDesc, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolGame::parseCancelWalk(const InputMessagePtr& msg)
|
void ProtocolGame::parseCancelWalk(const InputMessagePtr& msg)
|
||||||
|
|
Loading…
Reference in New Issue