Browse Source

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
Eduardo Bart 8 years ago
parent
commit
f289db3a9e

+ 0
- 1
modules/corelib/corelib.otmod View File

@@ -16,6 +16,5 @@ Module
16 16
     dofile 'settings'
17 17
     dofile 'keyboard'
18 18
     dofile 'mouse'
19
-    dofile 'protocol'
20 19
 
21 20
     dofiles 'ui'

+ 4
- 2
modules/corelib/settings.lua View File

@@ -51,11 +51,13 @@ function g_settings.getString(key, default)
51 51
 end
52 52
 
53 53
 function g_settings.getInteger(key, default)
54
-  return tonumber(g_settings.get(key, default))
54
+  local v = tonumber(g_settings.get(key, default)) or 1
55
+  return v
55 56
 end
56 57
 
57 58
 function g_settings.getNumber(key, default)
58
-  return tonumber(g_settings.get(key, default))
59
+  local v = tonumber(g_settings.get(key, default)) or 1
60
+  return v
59 61
 end
60 62
 
61 63
 function g_settings.getBoolean(key, default)

+ 26
- 8
modules/corelib/util.lua View File

@@ -110,9 +110,34 @@ function extends(base)
110 110
   return derived
111 111
 end
112 112
 
113
+function export(what, key)
114
+  if key ~= nil then
115
+    _G[key] = what
116
+  else
117
+    for k,v in pairs(what) do
118
+      _G[k] = v
119
+    end
120
+  end
121
+end
122
+
123
+function unexport(key)
124
+  if type(key) == 'table' then
125
+    for _k,v in pairs(key) do
126
+      _G[v] = nil
127
+    end
128
+  else
129
+    _G[key] = nil
130
+  end
131
+end
132
+
133
+function sandbox(what)
134
+  what = what or 2
135
+  setfenv(what, newenv())
136
+end
137
+
113 138
 function newenv()
114 139
   local env = { }
115
-  setmetatable(env, { __index = _G} )
140
+  setmetatable(env, { __index = getfenv() } )
116 141
   return env
117 142
 end
118 143
 
@@ -157,13 +182,6 @@ function toboolean(str)
157 182
   return false
158 183
 end
159 184
 
160
-local oldtonumber = tonumber
161
-
162
-function tonumber(v)
163
-  if v == nil then return 0 end
164
-  return oldtonumber(v)
165
-end
166
-
167 185
 function signalcall(param, ...)
168 186
   if type(param) == 'function' then
169 187
     return param(...)

+ 4
- 5
modules/game_playerdeath/playerdeath.lua View File

@@ -8,7 +8,7 @@ local deathWindow
8 8
 -- public functions
9 9
 function PlayerDeath.init()
10 10
   g_ui.importStyle('deathwindow.otui')
11
-  
11
+
12 12
   connect(g_game, { onDeath = PlayerDeath.display,
13 13
                     onGameEnd = PlayerDeath.reset })
14 14
 end
@@ -22,7 +22,6 @@ function PlayerDeath.terminate()
22 22
 end
23 23
 
24 24
 function PlayerDeath.reset()
25
-  GameInterface.getMapPanel():recursiveGetChildById('centerAdvance'):hide()
26 25
   if deathWindow then
27 26
     deathWindow:destroy()
28 27
     deathWindow = nil
@@ -37,9 +36,9 @@ end
37 36
 function PlayerDeath.displayDeadMessage()
38 37
   local advanceLabel = GameInterface.getMapPanel():recursiveGetChildById('centerAdvance')
39 38
   if advanceLabel:isVisible() then
40
-    return 
39
+    return
41 40
   end
42
-  
41
+
43 42
   TextMessage.displayEventAdvance(tr('You are dead.'))
44 43
 end
45 44
 
@@ -64,7 +63,7 @@ function PlayerDeath.openWindow()
64 63
 
65 64
   deathWindow.onEnter = okFunc
66 65
   deathWindow.onEscape = cancelFunc
67
-  
66
+
68 67
   okButton.onClick = okFunc
69 68
   cancelButton.onClick = cancelFunc
70 69
 end

+ 60
- 0
modules/game_textmessage/protocol.lua View File

@@ -0,0 +1,60 @@
1
+function getMessageTypes(version)
2
+  if version >= 960 then
3
+    perror("TODO: message types for 9.6")
4
+    return {}
5
+  elseif version >= 861 then
6
+    return {
7
+      [13] = 'ConsoleOrange',
8
+      [14] = 'ConsoleOrange',
9
+      [15] = 'Warning',
10
+      [16] = 'EventAdvance',
11
+      [17] = 'EventDefault',
12
+      [18] = 'StatusDefault',
13
+      [19] = 'Info',
14
+      [20] = 'StatusSmall',
15
+      [21] = 'ConsoleBlue',
16
+      [22] = 'ConsoleRed'
17
+    }
18
+  elseif version >= 854 then
19
+    return {
20
+      [18] = 'ConsoleRed',
21
+      [19] = 'ConsoleOrange',
22
+      [20] = 'ConsoleOrange',
23
+      [21] = 'Warning',
24
+      [22] = 'EventAdvance',
25
+      [23] = 'EventDefault',
26
+      [24] = 'StatusDefault',
27
+      [25] = 'Info',
28
+      [26] = 'StatusSmall',
29
+      [27] = 'ConsoleBlue'
30
+    }
31
+  else
32
+    return {
33
+      [18] = 'Warning',
34
+      [19] = 'EventAdvance',
35
+      [20] = 'EventDefault',
36
+      [21] = 'StatusDefault',
37
+      [22] = 'Info',
38
+      [23] = 'StatusSmall',
39
+      [24] = 'ConsoleBlue',
40
+      [25] = 'ConsoleRed',
41
+      [26] = 'ConsoleOrange',
42
+      [27] = 'ConsoleOrange',
43
+    }
44
+   end
45
+end
46
+
47
+function parseTextMessage(msg)
48
+  local msgtype = msg:getU8()
49
+  local text = msg:getString()
50
+  msgtype = getMessageTypes(g_game.getProtocolVersion())[msgtype]
51
+  signalcall(g_game.onTextMessage, msgtype, text)
52
+end
53
+
54
+function registerProtocol()
55
+  ProtocolGame.registerOpcode(GameServerOpcodes.GameServerTextMessage, parseTextMessage)
56
+end
57
+
58
+function unregisterProtocol()
59
+  ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerTextMessage)
60
+end

+ 87
- 94
modules/game_textmessage/textmessage.lua View File

@@ -1,65 +1,31 @@
1
-TextMessage = {}
2
-
3
-g_ui.importStyle('textmessage.otui')
4
-
5
-local MessageTypes = {
6
-  consoleRed      = { color = '#F55E5E', consoleTab = tr('Default') },
7
-  consoleOrange   = { color = '#FE6500', consoleTab = tr('Default') },
8
-  consoleBlue     = { color = '#9F9DFD', consoleTab = tr('Default') },
9
-  warning         = { color = '#F55E5E', consoleTab = tr('Server Log'), labelId = 'centerWarning' },
10
-  infoDescription = { color = '#00EB00', consoleTab = tr('Server Log'), labelId = 'centerInfo', consoleOption = 'showInfoMessagesInConsole' },
11
-  eventAdvance    = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'centerAdvance', consoleOption = 'showEventMessagesInConsole' },
12
-  eventDefault    = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'bottomStatus', consoleOption = 'showEventMessagesInConsole' },
13
-  statusDefault   = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'bottomStatus', consoleOption = 'showStatusMessagesInConsole' },
14
-  statusSmall     = { color = '#FFFFFF', labelId = 'bottomStatus' },
15
-  private         = { color = '#5FF7F7', labelId = 'centerPrivate' }
1
+MessageTypesConf = {
2
+  ConsoleRed      = { color = '#F55E5E', consoleTab = tr('Default') },
3
+  ConsoleOrange   = { color = '#FE6500', consoleTab = tr('Default') },
4
+  ConsoleBlue     = { color = '#9F9DFD', consoleTab = tr('Default') },
5
+  Warning         = { color = '#F55E5E', consoleTab = tr('Server Log'), labelId = 'warningLabel' },
6
+  Info            = { color = '#00EB00', consoleTab = tr('Server Log'), labelId = 'infoLabel', consoleOption = 'showInfoMessagesInConsole' },
7
+  EventAdvance    = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'advanceLabel', consoleOption = 'showEventMessagesInConsole' },
8
+  EventDefault    = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'statusLabel', consoleOption = 'showEventMessagesInConsole' },
9
+  StatusDefault   = { color = '#FFFFFF', consoleTab = tr('Server Log'), labelId = 'statusLabel', consoleOption = 'showStatusMessagesInConsole' },
10
+  StatusSmall     = { color = '#FFFFFF', labelId = 'statusLabel' },
11
+  Private         = { color = '#5FF7F7', labelId = 'privateLabel' }
16 12
 }
17 13
 
18
-local centerTextMessagePanel
19
-local bottomStatusLabel
20
-local privateLabel
21
-
22
-local function displayMessage(msgtype, msg, time)
23
-  if not g_game.isOnline() then return end
24
-
25
-  if msgtype.consoleTab ~= nil then
26
-    if msgtype.consoleOption == nil or Options.getOption(msgtype.consoleOption) then
27
-      Console.addText(msg, msgtype, msgtype.consoleTab)
28
-    end
29
-  end
30
-
31
-  if msgtype.labelId then
32
-    local label = GameInterface.getMapPanel():recursiveGetChildById(msgtype.labelId)
33
-
34
-    label:setText(msg)
35
-    label:setColor(msgtype.color)
36
-
37
-    if not time then
38
-      time = math.max(#msg * 100, 4000)
39
-    else
40
-      time = time * 1000
41
-    end
42
-    removeEvent(label.hideEvent)
43
-    addEvent(function() label:setVisible(true) end)
44
-    label.hideEvent = scheduleEvent(function() label:setVisible(false) end, time)
45
-  end
46
-end
47
-
48
-local function createTextMessageLabel(id, parent, class)
49
-  local label = g_ui.createWidget(class, parent)
50
-  label:setFont('verdana-11px-rounded')
51
-  label:setId(id)
52
-  return label
53
-end
54
-
55
-function TextMessage.init()
56
-  connect(g_game, { onTextMessage = TextMessage.display,
57
-                    onGameStart = TextMessage.clearMessages })
58
-
14
+centerTextMessagePanel = nil
15
+statusLabel = nil
16
+privateLabel = nil
17
+warningLabel = nil
18
+advanceLabel = nil
19
+infoLabel = nil
20
+
21
+function init()
22
+  connect(g_game, {
23
+    onTextMessage = displayMessage,
24
+    onGameStart = clearMessages
25
+  })
26
+  registerProtocol()
27
+
28
+  g_ui.importStyle('textmessage.otui')
59 29
   centerTextMessagePanel = g_ui.createWidget('Panel', GameInterface.getMapPanel())
60 30
   centerTextMessagePanel:setId('centerTextMessagePanel')
61 31
 
@@ -69,55 +35,78 @@ function TextMessage.init()
69 35
   centerTextMessagePanel:setWidth(360)
70 36
   centerTextMessagePanel:centerIn('parent')
71 37
 
72
-  createTextMessageLabel('centerWarning', centerTextMessagePanel, 'CenterLabel')
73
-  createTextMessageLabel('centerAdvance', centerTextMessagePanel, 'CenterLabel')
74
-  createTextMessageLabel('centerInfo', centerTextMessagePanel, 'CenterLabel')
75
-
76
-  privateLabel = createTextMessageLabel('centerPrivate', GameInterface.getMapPanel(), 'TopCenterLabel')
77
-  bottomStatusLabel = createTextMessageLabel('bottomStatus', GameInterface.getMapPanel(), 'BottomLabel')
38
+  warningLabel = createTextMessageLabel('warningLabel', centerTextMessagePanel, 'CenterLabel')
39
+  advanceLabel = createTextMessageLabel('advanceLabel', centerTextMessagePanel, 'CenterLabel')
40
+  infoLabel = createTextMessageLabel('infoLabel', centerTextMessagePanel, 'CenterLabel')
41
+  privateLabel = createTextMessageLabel('privateLabel', GameInterface.getMapPanel(), 'TopCenterLabel')
42
+  statusLabel = createTextMessageLabel('statusLabel', GameInterface.getMapPanel(), 'BottomLabel')
43
+
44
+  export({
45
+    clearMessages = clearMessages,
46
+    displayStatus = function() displayMessage(MessageTypes.StatusSmall, msg) end,
47
+    displayEventAdvance = function() displayMessage(MessageTypes.EventAdvance, msg, time) end,
48
+    displayPrivate = function() displayPrivate(msg, time) end
49
+  }, 'TextMessage')
78 50
 end
79 51
 
80
-function TextMessage.terminate()
81
-  disconnect(g_game, { onDeath = TextMessage.displayDeadMessage,
82
-                       onTextMessage = TextMessage.display,
83
-                       onGameStart = TextMessage.clearMessages })
84
-  removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerWarning').hideEvent)
85
-  removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerAdvance').hideEvent)
86
-  removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerInfo').hideEvent)
87
-  removeEvent(GameInterface.getMapPanel():recursiveGetChildById('centerPrivate').hideEvent)
88
-  removeEvent(GameInterface.getMapPanel():recursiveGetChildById('bottomStatus').hideEvent)
52
+function terminate()
53
+  disconnect(g_game, {
54
+    onTextMessage = display,
55
+    onGameStart = clearMessages
56
+  })
57
+  unregisterProtocol()
58
+
59
+  removeEvent(warningLabel.hideEvent)
60
+  removeEvent(advanceLabel.hideEvent)
61
+  removeEvent(infoLabel.hideEvent)
62
+  removeEvent(privateLabel.hideEvent)
63
+  removeEvent(statusLabel.hideEvent)
64
+
89 65
   centerTextMessagePanel:destroy()
90
-  bottomStatusLabel:destroy()
66
+  statusLabel:destroy()
91 67
   privateLabel:destroy()
92
-  centerTextMessagePanel = nil
93
-  bottomStatusLabel = nil
94
-  privateLabel = nil
95
-  TextMessage = nil
96
-end
97 68
 
98
-function TextMessage.clearMessages()
99
-  GameInterface.getMapPanel():recursiveGetChildById('centerWarning'):hide()
100
-  GameInterface.getMapPanel():recursiveGetChildById('centerAdvance'):hide()
101
-  GameInterface.getMapPanel():recursiveGetChildById('centerInfo'):hide()
102
-  GameInterface.getMapPanel():recursiveGetChildById('centerPrivate'):hide()
103
-  GameInterface.getMapPanel():recursiveGetChildById('bottomStatus'):hide()
69
+  unexport('TextMessage')
104 70
 end
105 71
 
106
-function TextMessage.displayStatus(msg, time)
107
-  displayMessage(MessageTypes.statusSmall, msg)
72
+function clearMessages()
73
+  warningLabel:hide()
74
+  advanceLabel:hide()
75
+  infoLabel:hide()
76
+  privateLabel:hide()
77
+  statusLabel:hide()
108 78
 end
109 79
 
110
-function TextMessage.displayEventAdvance(msg, time)
111
-  displayMessage(MessageTypes.eventAdvance, msg, time)
80
+function createTextMessageLabel(id, parent, class)
81
+  local label = g_ui.createWidget(class, parent)
82
+  label:setFont('verdana-11px-rounded')
83
+  label:setId(id)
84
+  return label
112 85
 end
113 86
 
114
-function TextMessage.displayPrivate(msg, time)
115
-  displayMessage(MessageTypes.private, msg, time)
116
-end
87
+function displayMessage(msgtype, msg, time)
88
+  if not g_game.isOnline() then return end
89
+  msgtypeconf = MessageTypesConf[msgtype]
117 90
 
118
-function TextMessage.display(msgtypedesc, msg)
119
-  local msgtype = MessageTypes[msgtypedesc]
120
-  if msgtype then
121
-    displayMessage(msgtype, msg)
91
+  if msgtypeconf.consoleTab ~= nil then
92
+    if msgtypeconf.consoleOption == nil or Options.getOption(msgtypeconf.consoleOption) then
93
+      Console.addText(msg, msgtypeconf, msgtypeconf.consoleTab)
94
+    end
95
+  end
96
+
97
+  if msgtypeconf.labelId then
98
+    local label = GameInterface.getMapPanel():recursiveGetChildById(msgtypeconf.labelId)
99
+
100
+    label:setText(msg)
101
+    label:setColor(msgtypeconf.color)
102
+
103
+    if not time then
104
+      time = math.max(#msg * 100, 4000)
105
+    else
106
+      time = time * 1000
107
+    end
108
+    removeEvent(label.hideEvent)
109
+    addEvent(function() label:setVisible(true) end)
110
+    label.hideEvent = scheduleEvent(function() label:setVisible(false) end, time)
122 111
   end
123 112
 end

+ 6
- 5
modules/game_textmessage/textmessage.otmod View File

@@ -3,13 +3,14 @@ Module
3 3
   description: Manage game text messages
4 4
   author: edubart
5 5
   website: www.otclient.info
6
+  sandboxed: true
6 7
 
7 8
   dependencies:
8 9
     - game_interface
9 10
 
10
-  @onLoad: |
11
-    dofile 'textmessage'
12
-    TextMessage.init()
11
+  scripts:
12
+    - protocol.lua
13
+    - textmessage.lua
13 14
 
14
-  @onUnload: |
15
-    TextMessage.terminate()
15
+  @onLoad: init()
16
+  @onUnload: terminate()

+ 1
- 1
modules/gamelib/gamelib.otmod View File

@@ -10,7 +10,7 @@ Module
10 10
 
11 11
   @onLoad: |
12 12
     dofile 'const'
13
-
13
+    dofile 'protocol'
14 14
     dofile 'protocollogin'
15 15
     dofile 'protocolgame'
16 16
     dofile 'game'

modules/corelib/protocol.lua → modules/gamelib/protocol.lua View File


+ 59
- 26
src/framework/core/module.cpp View File

@@ -22,6 +22,7 @@
22 22
 
23 23
 #include "module.h"
24 24
 #include "modulemanager.h"
25
+#include "resourcemanager.h"
25 26
 
26 27
 #include <framework/otml/otml.h>
27 28
 #include <framework/luaengine/luainterface.h>
@@ -29,6 +30,8 @@
29 30
 Module::Module(const std::string& name)
30 31
 {
31 32
     m_name = name;
33
+    g_lua.newEnvironment();
34
+    m_sandboxEnv = g_lua.ref();
32 35
 }
33 36
 
34 37
 bool Module::load()
@@ -36,24 +39,43 @@ bool Module::load()
36 39
     if(m_loaded)
37 40
         return true;
38 41
 
39
-    for(const std::string& depName : m_dependencies) {
40
-        ModulePtr dep = g_modules.getModule(depName);
41
-        if(!dep) {
42
-            g_logger.error(stdext::format("Unable to load module '%s' because dependency '%s' was not found", m_name, depName));
43
-            return false;
42
+    try {
43
+        for(const std::string& depName : m_dependencies) {
44
+            ModulePtr dep = g_modules.getModule(depName);
45
+            if(!dep)
46
+                stdext::throw_exception(stdext::format("dependency '%s' was not found", m_name, depName));
47
+
48
+            if(!dep->isLoaded() && !dep->load())
49
+                stdext::throw_exception(stdext::format("dependency '%s' has failed to load", m_name, depName));
44 50
         }
45 51
 
46
-        if(!dep->isLoaded() && !dep->load()) {
47
-            g_logger.error(stdext::format("Unable to load module '%s' because dependency '%s' has failed to load", m_name, depName));
48
-            return false;
52
+        for(const std::string& script : m_scripts) {
53
+            g_lua.loadScript(script);
54
+            if(m_sandboxed) {
55
+                g_lua.getRef(m_sandboxEnv);
56
+                g_lua.setEnv();
57
+            }
58
+            g_lua.safeCall(0, 0);
59
+        }
60
+
61
+        const std::string& onLoadBuffer = std::get<0>(m_onLoadFunc);
62
+        const std::string& onLoadSource = std::get<1>(m_onLoadFunc);
63
+        if(!onLoadBuffer.empty()) {
64
+            g_lua.loadBuffer(onLoadBuffer, onLoadSource);
65
+            if(m_sandboxed) {
66
+                g_lua.getRef(m_sandboxEnv);
67
+                g_lua.setEnv();
68
+            }
69
+            g_lua.safeCall(0, 0);
49 70
         }
50
-    }
51 71
 
52
-    if(m_loadCallback)
53
-        m_loadCallback();
72
+        g_logger.debug(stdext::format("Loaded module '%s'", m_name));
73
+    } catch(stdext::exception& e) {
74
+        g_logger.error(stdext::format("Unable to load module '%s': %s", m_name, e.what()));
75
+        return false;
76
+    }
54 77
 
55 78
     m_loaded = true;
56
-    g_logger.debug(stdext::format("Loaded module '%s'", m_name));
57 79
     g_modules.updateModuleLoadOrder(asModule());
58 80
 
59 81
     for(const std::string& modName : m_loadLaterModules) {
@@ -70,8 +92,21 @@ bool Module::load()
70 92
 void Module::unload()
71 93
 {
72 94
     if(m_loaded) {
73
-        if(m_unloadCallback)
74
-            m_unloadCallback();
95
+        try {
96
+            const std::string& onUnloadBuffer = std::get<0>(m_onUnloadFunc);
97
+            const std::string& onUnloadSource = std::get<1>(m_onUnloadFunc);
98
+            if(!onUnloadBuffer.empty()) {
99
+                g_lua.loadBuffer(onUnloadBuffer, onUnloadSource);
100
+                if(m_sandboxed) {
101
+                    g_lua.getRef(m_sandboxEnv);
102
+                    g_lua.setEnv();
103
+                }
104
+                g_lua.safeCall(0, 0);
105
+            }
106
+        } catch(stdext::exception& e) {
107
+            g_logger.error(stdext::format("Unable to unload module '%s': %s", m_name, e.what()));
108
+        }
109
+
75 110
         m_loaded = false;
76 111
         //g_logger.info(stdext::format("Unloaded module '%s'", m_name));
77 112
         g_modules.updateModuleLoadOrder(asModule());
@@ -109,6 +144,7 @@ void Module::discover(const OTMLNodePtr& moduleNode)
109 144
     m_version = moduleNode->valueAt("version", none);
110 145
     m_autoLoad = moduleNode->valueAt<bool>("autoload", false);
111 146
     m_reloadable = moduleNode->valueAt<bool>("reloadable", true);
147
+    m_sandboxed = moduleNode->valueAt<bool>("sandboxed", false);
112 148
     m_autoLoadPriority = moduleNode->valueAt<int>("autoload-priority", 9999);
113 149
 
114 150
     if(OTMLNodePtr node = moduleNode->get("dependencies")) {
@@ -116,22 +152,19 @@ void Module::discover(const OTMLNodePtr& moduleNode)
116 152
             m_dependencies.push_back(tmp->value());
117 153
     }
118 154
 
119
-    // set onLoad callback
120
-    if(OTMLNodePtr node = moduleNode->get("@onLoad")) {
121
-        g_lua.loadFunction(node->value(), "@" + node->source() + "[" + node->tag() + "]");
122
-        g_lua.useValue();
123
-        m_loadCallback = g_lua.polymorphicPop<std::function<void()>>();
124
-    }
125
-
126
-    // set onUnload callback
127
-    if(OTMLNodePtr node = moduleNode->get("@onUnload")) {
128
-        g_lua.loadFunction(node->value(), "@" + node->source() + "[" + node->tag() + "]");
129
-        g_lua.useValue();
130
-        m_unloadCallback = g_lua.polymorphicPop<std::function<void()>>();
155
+    if(OTMLNodePtr node = moduleNode->get("scripts")) {
156
+        for(const OTMLNodePtr& tmp : node->children())
157
+            m_scripts.push_back(stdext::resolve_path(tmp->value(), node->source()));
131 158
     }
132 159
 
133 160
     if(OTMLNodePtr node = moduleNode->get("load-later")) {
134 161
         for(const OTMLNodePtr& tmp : node->children())
135 162
             m_loadLaterModules.push_back(tmp->value());
136 163
     }
164
+
165
+    if(OTMLNodePtr node = moduleNode->get("@onLoad"))
166
+        m_onLoadFunc = std::make_tuple(node->value(), "@" + node->source() + "[" + node->tag() + "]");
167
+
168
+    if(OTMLNodePtr node = moduleNode->get("@onUnload"))
169
+        m_onUnloadFunc = std::make_tuple(node->value(), "@" + node->source() + "[" + node->tag() + "]");
137 170
 }

+ 5
- 0
src/framework/core/module.h View File

@@ -64,7 +64,11 @@ private:
64 64
     Boolean<false> m_loaded;
65 65
     Boolean<false> m_autoLoad;
66 66
     Boolean<false> m_reloadable;
67
+    Boolean<false> m_sandboxed;
67 68
     int m_autoLoadPriority;
69
+    int m_sandboxEnv;
70
+    std::tuple<std::string, std::string> m_onLoadFunc;
71
+    std::tuple<std::string, std::string> m_onUnloadFunc;
68 72
     std::string m_name;
69 73
     std::string m_description;
70 74
     std::string m_author;
@@ -73,6 +77,7 @@ private:
73 77
     std::function<void()> m_loadCallback;
74 78
     std::function<void()> m_unloadCallback;
75 79
     std::list<std::string> m_dependencies;
80
+    std::list<std::string> m_scripts;
76 81
     std::list<std::string> m_loadLaterModules;
77 82
 };
78 83
 

+ 25
- 14
src/framework/luaengine/luainterface.cpp View File

@@ -306,13 +306,13 @@ bool LuaInterface::safeRunScript(const std::string& fileName)
306 306
 void LuaInterface::runScript(const std::string& fileName)
307 307
 {
308 308
     loadScript(fileName);
309
-    safeCall();
309
+    safeCall(0, 0);
310 310
 }
311 311
 
312 312
 void LuaInterface::runBuffer(const std::string& buffer, const std::string& source)
313 313
 {
314 314
     loadBuffer(buffer, source);
315
-    safeCall();
315
+    safeCall(0, 0);
316 316
 }
317 317
 
318 318
 void LuaInterface::loadScript(const std::string& fileName)
@@ -425,7 +425,7 @@ std::string LuaInterface::getCurrentSourcePath(int level)
425 425
     return path;
426 426
 }
427 427
 
428
-int LuaInterface::safeCall(int numArgs)
428
+int LuaInterface::safeCall(int numArgs, int numRets)
429 429
 {
430 430
     assert(hasIndex(-numArgs-1));
431 431
 
@@ -446,22 +446,33 @@ int LuaInterface::safeCall(int numArgs)
446 446
     if(ret != 0)
447 447
         throw LuaException(popString());
448 448
 
449
+    int rets = (stackSize() + numArgs + 1) - previousStackSize;
450
+    while(numRets != -1 && rets != numRets) {
451
+        if(rets < numRets) {
452
+            pushNil();
453
+            rets++;
454
+        } else {
455
+            pop();
456
+            rets--;
457
+        }
458
+    }
459
+
449 460
     // returns the number of results
450
-    return (stackSize() + numArgs + 1) - previousStackSize;
461
+    return rets;
451 462
 }
452 463
 
453
-int LuaInterface::signalCall(int numArgs, int requestedResults)
464
+int LuaInterface::signalCall(int numArgs, int numRets)
454 465
 {
455
-    int numRets = 0;
466
+    int rets = 0;
456 467
     int funcIndex = -numArgs-1;
457 468
 
458 469
     try {
459 470
         // must be a function
460 471
         if(isFunction(funcIndex)) {
461
-            numRets = safeCall(numArgs);
472
+            rets = safeCall(numArgs);
462 473
 
463
-            if(requestedResults != -1) {
464
-                if(numRets != requestedResults)
474
+            if(numRets != -1) {
475
+                if(rets != numRets)
465 476
                     throw LuaException("function call didn't return the expected number of results", 0);
466 477
             }
467 478
         }
@@ -491,8 +502,8 @@ int LuaInterface::signalCall(int numArgs, int requestedResults)
491 502
             }
492 503
             pop(numArgs + 1); // pops the table of function and arguments
493 504
 
494
-            if(requestedResults == 1) {
495
-                numRets = 1;
505
+            if(numRets == 1) {
506
+                rets = 1;
496 507
                 pushBoolean(done);
497 508
             }
498 509
         }
@@ -509,13 +520,13 @@ int LuaInterface::signalCall(int numArgs, int requestedResults)
509 520
     }
510 521
 
511 522
     // pushes nil values if needed
512
-    while(requestedResults != -1 && numRets < requestedResults) {
523
+    while(numRets != -1 && rets < numRets) {
513 524
         pushNil();
514
-        numRets++;
525
+        rets++;
515 526
     }
516 527
 
517 528
     // returns the number of results on the stack
518
-    return numRets;
529
+    return rets;
519 530
 }
520 531
 
521 532
 void LuaInterface::newEnvironment()

+ 2
- 2
src/framework/luaengine/luainterface.h View File

@@ -174,13 +174,13 @@ public:
174 174
     /// results are pushed onto the stack.
175 175
     /// @exception LuaException is thrown on any lua error
176 176
     /// @return number of results
177
-    int safeCall(int numArgs = 0);
177
+    int safeCall(int numArgs = 0, int numRets = -1);
178 178
 
179 179
     /// Same as safeCall but catches exceptions and can also calls a table of functions,
180 180
     /// if any error occurs it will be reported to stdout and returns 0 results
181 181
     /// @param requestedResults is the number of results requested to pushes onto the stack,
182 182
     /// if supplied, the call will always pushes that number of results, even if it fails
183
-    int signalCall(int numArgs = 0, int requestedResults = -1);
183
+    int signalCall(int numArgs = 0, int numRets = -1);
184 184
 
185 185
     /// @brief Creates a new environment table
186 186
     /// The new environment table is redirected to the global environment (aka _G),

+ 1
- 1
src/otclient/game.h View File

@@ -60,7 +60,7 @@ protected:
60 60
     void processWalkCancel(Otc::Direction direction);
61 61
 
62 62
     // message related
63
-    void processTextMessage(const std::string& type, const std::string& message);
63
+    void processTextMessage(const std::string& type, const std::string& message); // deprecated
64 64
     void processCreatureSpeak(const std::string& name, int level, Otc::SpeakType type, const std::string& message, int channelId, const Position& creaturePos);
65 65
 
66 66
     // container related

+ 0
- 1
src/otclient/map.cpp View File

@@ -556,7 +556,6 @@ void Map::clean()
556 556
     m_towns.clear();
557 557
     m_houses.clear();
558 558
     m_creatures.clear();
559
-    m_monsters.clear();
560 559
     m_tilesRect = Rect(65534, 65534, 0, 0);
561 560
 }
562 561
 

+ 0
- 89
src/otclient/protocolcodes.h View File

@@ -329,77 +329,6 @@ namespace Proto {
329 329
 #endif
330 330
     };
331 331
 
332
-    enum MessageTypes {
333
-#if PROTOCOL>=910
334
-        // 1-3
335
-        MessageConsoleBlue = 4,         // old
336
-        // 5-11
337
-        MessageConsoleRed = 12,         // old
338
-        // 13-15
339
-        MessageStatusDefault = 16,      // old
340
-        MessageWarning,                 // old
341
-        MessageEventAdvance,            // old
342
-        MessageStatusSmall,             // old
343
-        MessageInfoDescription,         // old
344
-        MessageDamageDealt,             // new
345
-        MessageDamageReceived,          // new
346
-        MessageHealed,                  // new
347
-        MessageExperience,              // new
348
-        MessageDamageOthers,            // new
349
-        MessageHealedOthers,            // new
350
-        MessageExperienceOthers,        // new
351
-        MessageEventDefault,            // old
352
-        MessageLoot,                    // new
353
-        MessageTradeNpc,                // unused
354
-        MessageChannelGuild,            // new
355
-        MessagePartyManagment,          // unused
356
-        MessageParty,                   // unused
357
-        MessageEventOrange,             // old
358
-        MessageConsoleOrange,           // old
359
-        MessageReport,                  // unused
360
-        MessageHotkeyUse,               // unused
361
-        MessageTutorialHint,            // unused
362
-
363
-        // unsupported
364
-        MessageConsoleOrange2 = 255
365
-#elif PROTOCOL>=861
366
-        MessageConsoleOrange = 13,
367
-        MessageConsoleOrange2,
368
-        MessageWarning,
369
-        MessageEventAdvance,
370
-        MessageEventDefault,
371
-        MessageStatusDefault,
372
-        MessageInfoDescription,
373
-        MessageStatusSmall,
374
-        MessageConsoleBlue,
375
-        MessageConsoleRed
376
-#elif PROTOCOL>=854
377
-        MessageConsoleRed = 18,
378
-        MessageConsoleOrange,
379
-        MessageConsoleOrange2,
380
-        MessageWarning,
381
-        MessageEventAdvance,
382
-        MessageEventDefault,
383
-        MessageStatusDefault,
384
-        MessageInfoDescription,
385
-        MessageStatusSmall,
386
-        MessageConsoleBlue
387
-#elif PROTOCOL>=810
388
-        MessageWarning = 18,
389
-        MessageEventAdvance,
390
-        MessageEventDefault,
391
-        MessageStatusDefault,
392
-        MessageInfoDescription,
393
-        MessageStatusSmall,
394
-        MessageConsoleBlue,
395
-        MessageConsoleRed,
396
-
397
-        // unsupported
398
-        MessageConsoleOrange = 255,
399
-        MessageConsoleOrange2,
400
-#endif
401
-    };
402
-
403 332
     enum CreatureType {
404 333
         CreatureTypePlayer = 0,
405 334
         CreatureTypeMonster,
@@ -459,24 +388,6 @@ namespace Proto {
459 388
                 return Proto::ServerSpeakSay;
460 389
         }
461 390
     }
462
-
463
-    inline std::string translateTextMessageType(int type) {
464
-        switch(type) {
465
-            case Proto::MessageConsoleOrange: return "consoleOrange";
466
-            case Proto::MessageConsoleOrange2: return "consoleOrange";
467
-            case Proto::MessageWarning: return "warning";
468
-            case Proto::MessageEventAdvance: return "eventAdvance";
469
-            case Proto::MessageEventDefault: return "eventDefault";
470
-            case Proto::MessageStatusDefault: return "statusDefault";
471
-            case Proto::MessageInfoDescription: return "infoDescription";
472
-            case Proto::MessageStatusSmall: return "statusSmall";
473
-            case Proto::MessageConsoleBlue: return "consoleBlue";
474
-            case Proto::MessageConsoleRed: return "consoleRed";
475
-            default:
476
-                g_logger.error(stdext::format("unknown protocol text message type %d", type));
477
-                return "unknown";
478
-        }
479
-    }
480 391
 }
481 392
 
482 393
 #endif

+ 3
- 6
src/otclient/protocolgameparse.cpp View File

@@ -1067,12 +1067,9 @@ void ProtocolGame::parseRuleViolationLock(const InputMessagePtr& msg)
1067 1067
 
1068 1068
 void ProtocolGame::parseTextMessage(const InputMessagePtr& msg)
1069 1069
 {
1070
-    int type = msg->getU8();
1071
-
1072
-    std::string typeDesc = Proto::translateTextMessageType(type);
1073
-    std::string message = msg->getString();
1074
-
1075
-    g_game.processTextMessage(typeDesc, message);
1070
+    msg->getU8(); // type
1071
+    msg->getString(); // message
1072
+    // this is now handled by game_textmessage module
1076 1073
 }
1077 1074
 
1078 1075
 void ProtocolGame::parseCancelWalk(const InputMessagePtr& msg)

Loading…
Cancel
Save