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
master
Eduardo Bart 12 years ago
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,65 +1,31 @@
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
-- private functions advanceLabel = nil
local function displayMessage(msgtype, msg, time) infoLabel = nil
if not g_game.isOnline() then return end
function init()
if msgtype.consoleTab ~= nil then connect(g_game, {
if msgtype.consoleOption == nil or Options.getOption(msgtype.consoleOption) then onTextMessage = displayMessage,
Console.addText(msg, msgtype, msgtype.consoleTab) onGameStart = clearMessages
end })
end registerProtocol()
if msgtype.labelId then g_ui.importStyle('textmessage.otui')
local label = GameInterface.getMapPanel():recursiveGetChildById(msgtype.labelId)
label:setText(msg)
label:setColor(msgtype.color)
if not time then
time = math.max(#msg * 100, 4000)
else
time = time * 1000
end
removeEvent(label.hideEvent)
addEvent(function() label:setVisible(true) end)
label.hideEvent = scheduleEvent(function() label:setVisible(false) end, time)
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 = g_ui.createWidget('Panel', GameInterface.getMapPanel())
centerTextMessagePanel:setId('centerTextMessagePanel') centerTextMessagePanel:setId('centerTextMessagePanel')
@ -69,55 +35,78 @@ function TextMessage.init()
centerTextMessagePanel:setWidth(360) centerTextMessagePanel:setWidth(360)
centerTextMessagePanel:centerIn('parent') centerTextMessagePanel:centerIn('parent')
createTextMessageLabel('centerWarning', centerTextMessagePanel, 'CenterLabel') warningLabel = createTextMessageLabel('warningLabel', centerTextMessagePanel, 'CenterLabel')
createTextMessageLabel('centerAdvance', centerTextMessagePanel, 'CenterLabel') advanceLabel = createTextMessageLabel('advanceLabel', centerTextMessagePanel, 'CenterLabel')
createTextMessageLabel('centerInfo', centerTextMessagePanel, 'CenterLabel') infoLabel = createTextMessageLabel('infoLabel', centerTextMessagePanel, 'CenterLabel')
privateLabel = createTextMessageLabel('privateLabel', GameInterface.getMapPanel(), 'TopCenterLabel')
privateLabel = createTextMessageLabel('centerPrivate', GameInterface.getMapPanel(), 'TopCenterLabel') statusLabel = createTextMessageLabel('statusLabel', GameInterface.getMapPanel(), 'BottomLabel')
bottomStatusLabel = createTextMessageLabel('bottomStatus', 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 end
function TextMessage.terminate() function terminate()
disconnect(g_game, { onDeath = TextMessage.displayDeadMessage, disconnect(g_game, {
onTextMessage = TextMessage.display, onTextMessage = display,
onGameStart = TextMessage.clearMessages }) onGameStart = clearMessages
removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerWarning').hideEvent) })
removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerAdvance').hideEvent) unregisterProtocol()
removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerInfo').hideEvent)
removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerPrivate').hideEvent) removeEvent(warningLabel.hideEvent)
removeEvent(GameInterface.getMapPanel():recursiveGetChildById('bottomStatus').hideEvent) removeEvent(advanceLabel.hideEvent)
removeEvent(infoLabel.hideEvent)
removeEvent(privateLabel.hideEvent)
removeEvent(statusLabel.hideEvent)
centerTextMessagePanel:destroy() centerTextMessagePanel:destroy()
bottomStatusLabel:destroy() statusLabel:destroy()
privateLabel:destroy() privateLabel:destroy()
centerTextMessagePanel = nil
bottomStatusLabel = nil
privateLabel = nil
TextMessage = nil
end
function TextMessage.clearMessages() unexport('TextMessage')
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 end
function TextMessage.displayStatus(msg, time) function clearMessages()
displayMessage(MessageTypes.statusSmall, msg) warningLabel:hide()
advanceLabel:hide()
infoLabel:hide()
privateLabel:hide()
statusLabel:hide()
end end
function TextMessage.displayEventAdvance(msg, time) function createTextMessageLabel(id, parent, class)
displayMessage(MessageTypes.eventAdvance, msg, time) local label = g_ui.createWidget(class, parent)
label:setFont('verdana-11px-rounded')
label:setId(id)
return label
end end
function TextMessage.displayPrivate(msg, time) function displayMessage(msgtype, msg, time)
displayMessage(MessageTypes.private, msg, time) if not g_game.isOnline() then return end
end msgtypeconf = MessageTypesConf[msgtype]
function TextMessage.display(msgtypedesc, msg) if msgtypeconf.consoleTab ~= nil then
local msgtype = MessageTypes[msgtypedesc] if msgtypeconf.consoleOption == nil or Options.getOption(msgtypeconf.consoleOption) then
if msgtype then Console.addText(msg, msgtypeconf, msgtypeconf.consoleTab)
displayMessage(msgtype, msg) end
end
if msgtypeconf.labelId then
local label = GameInterface.getMapPanel():recursiveGetChildById(msgtypeconf.labelId)
label:setText(msg)
label:setColor(msgtypeconf.color)
if not time then
time = math.max(#msg * 100, 4000)
else
time = time * 1000
end
removeEvent(label.hideEvent)
addEvent(function() label:setVisible(true) end)
label.hideEvent = scheduleEvent(function() label:setVisible(false) end, time)
end end
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);
} }
}
if(m_loadCallback) g_logger.debug(stdext::format("Loaded module '%s'", m_name));
m_loadCallback(); } catch(stdext::exception& e) {
g_logger.error(stdext::format("Unable to load module '%s': %s", m_name, e.what()));
return false;
}
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…
Cancel
Save