a lot of changes in modules

This commit is contained in:
Eduardo Bart 2012-02-06 17:19:47 -02:00
parent add8505a5b
commit 88301c329a
50 changed files with 488 additions and 322 deletions

3
addons/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*
!/README.txt
!.gitignore

1
addons/README.txt Normal file
View File

@ -0,0 +1 @@
You can plance any personal addons here.

View File

@ -1,24 +0,0 @@
Module
name: client
description: Load all other otclient dependecies
author: OTClient team
website: https://github.com/edubart/otclient
canUnload: false
// NOTE: order does matter
dependencies:
- client_background
- client_topmenu
- client_tibiafiles
- client_about
- client_options
- client_entergame
- client_modulemanager
- game
onLoad: |
dofile 'client'
Client.init()
onUnload: |
Client.terminate()

View File

@ -3,6 +3,12 @@ Module
description: Create the about window
author: OTClient team
website: https://github.com/edubart/otclient
autoload: true
autoload-antecedence: 160
unloadable: true
dependencies:
- client_topmenu
onLoad: |
dofile 'about'

View File

@ -3,6 +3,8 @@ Module
description: Handles the background of the login screen
author: OTClient team
website: https://github.com/edubart/otclient
autoload: true
autoload-antecedence: 110
onLoad: |
dofile 'background'

View File

@ -42,11 +42,11 @@ local function tryLogin(charInfo, tries)
Game.loginWorld(EnterGame.account, EnterGame.password, charInfo.worldHost, charInfo.worldPort, charInfo.characterName)
loadBox = displayCancelBox('Please wait', 'Connecting to game server...')
function loadBox.onCancel()
loadBox = nil
Game.cancelLogin()
CharacterList.show()
end
connect(loadBox, { onCancel = function()
loadBox = nil
Game.cancelLogin()
CharacterList.show()
end })
-- save last used character
Settings.set('lastUsedCharacter', charInfo.characterName)

View File

@ -8,7 +8,6 @@ local motdMessage
local motdButton
local enterGameButton
-- private functions
local function clearAccountFields()
enterGame:getChildById('accountNameLineEdit'):clearText()
@ -20,11 +19,13 @@ end
local function onError(protocol, message, connectionError)
loadBox:destroy()
loadBox = nil
if not connectionError then
clearAccountFields()
end
local errorBox = displayErrorBox('Login Error', message)
errorBox.onOk = EnterGame.show
connect(errorBox, { onOk = EnterGame.show })
end
local function onMotd(protocol, motd)
@ -43,13 +44,15 @@ local function onCharacterList(protocol, characters, premDays)
end
loadBox:destroy()
loadBox = nil
CharacterList.create(characters, premDays)
local lastMotdNumber = Settings.getNumber("motd")
if motdNumber and motdNumber ~= lastMotdNumber then
Settings.set("motd", motdNumber)
local motdBox = displayInfoBox("Message of the day", motdMessage)
motdBox.onOk = CharacterList.show
connect(motdBox, { onOk = CharacterList.show })
CharacterList.hide()
end
end
@ -76,8 +79,13 @@ function EnterGame.init()
enterGame:getChildById('rememberPasswordBox'):setChecked(#account > 0)
enterGame:getChildById('accountNameLineEdit'):focus()
if #account > 0 and autologin then
addEvent(EnterGame.doLogin)
-- only open entergame when app starts
if not g_app.isRunning() then
if #account > 0 and autologin then
addEvent(EnterGame.doLogin)
end
else
enterGame:hide()
end
end
@ -124,10 +132,11 @@ function EnterGame.doLogin()
protocolLogin.onCharacterList = onCharacterList
loadBox = displayCancelBox('Please wait', 'Connecting to login server...')
loadBox.onCancel = function(msgbox)
protocolLogin:cancelLogin()
EnterGame.show()
end
connect(loadBox, { onCancel = function(msgbox)
loadBox = nil
protocolLogin:cancelLogin()
EnterGame.show()
end })
protocolLogin:login(EnterGame.host, EnterGame.port, EnterGame.account, EnterGame.password)
end

View File

@ -3,6 +3,8 @@ Module
description: Manages enter game and character list windows
author: OTClient team
website: https://github.com/edubart/otclient
autoload: true
autoload-antecedence: 150
onLoad: |
dofile 'entergame'

View File

@ -34,5 +34,4 @@ function Client.terminate()
Settings.set('window-size', g_window.getUnmaximizedSize())
Settings.set('window-pos', g_window.getUnmaximizedPos())
Settings.set('window-maximized', g_window.isMaximized())
g_window.hide()
end

View File

@ -0,0 +1,14 @@
Module
name: client_main
description: Initialize the client and setups its main window
author: OTClient team
website: https://github.com/edubart/otclient
autoload: true
autoload-antecedence: 100
onLoad: |
dofile 'client'
Client.init()
onUnload: |
Client.terminate()

View File

Before

Width:  |  Height:  |  Size: 518 B

After

Width:  |  Height:  |  Size: 518 B

View File

@ -54,25 +54,40 @@ function ModuleManager.refreshModules()
end
function ModuleManager.listModules()
if not moduleManagerWindow then return end
moduleList:destroyChildren()
local modules = g_modules.getModules()
for i,module in ipairs(modules) do
local label = createWidget('ModuleListLabel', moduleList)
label:setText(module:getName())
label:setOn(module:isLoaded())
end
moduleList:focusChild(moduleList:getFirstChild(), ActiveFocusReason)
end
function ModuleManager.refreshLoadedModules()
if not moduleManagerWindow then return end
for i,child in ipairs(moduleList:getChildren()) do
local module = g_modules.getModule(child:getText())
child:setOn(module:isLoaded())
end
end
function ModuleManager.updateModuleInfo(moduleName)
if not moduleManagerWindow then return end
local name = ''
local description = ''
local autoLoad = ''
local author = ''
local website = ''
local version = ''
local canLoad = false
local loaded = false
local canReload = false
local canUnload = false
local module = g_modules.getModule(moduleName)
@ -82,8 +97,9 @@ function ModuleManager.updateModuleInfo(moduleName)
author = module:getAuthor()
website = module:getWebsite()
version = module:getVersion()
canUnload = module:isLoaded()
canLoad = not canUnload
loaded = module:isLoaded()
canUnload = module:canUnload()
canReload = not loaded or canUnload
end
moduleManagerWindow:recursiveGetChildById('moduleName'):setText(name)
@ -93,17 +109,26 @@ function ModuleManager.updateModuleInfo(moduleName)
moduleManagerWindow:recursiveGetChildById('moduleWebsite'):setText(website)
moduleManagerWindow:recursiveGetChildById('moduleVersion'):setText(version)
moduleManagerWindow:recursiveGetChildById('moduleLoadButton'):setEnabled(canLoad)
moduleManagerWindow:recursiveGetChildById('moduleUnloadButton'):setEnabled(canUnload)
local reloadButton = moduleManagerWindow:recursiveGetChildById('moduleReloadButton')
reloadButton:setEnabled(canReload)
reloadButton:setVisible(true)
if loaded then reloadButton:setText('Reload')
else reloadButton:setText('Load') end
local unloadButton = moduleManagerWindow:recursiveGetChildById('moduleUnloadButton')
unloadButton:setVisible(true)
unloadButton:setEnabled(canUnload)
end
function ModuleManager.loadCurrentModule()
function ModuleManager.reloadCurrentModule()
local focusedChild = moduleList:getFocusedChild()
if focusedChild then
local module = g_modules.getModule(focusedChild:getText())
if module then
module:load()
module:reload()
ModuleManager.updateModuleInfo(module:getName())
ModuleManager.refreshLoadedModules()
ModuleManager.show()
end
end
end
@ -115,7 +140,14 @@ function ModuleManager.unloadCurrentModule()
if module then
module:unload()
ModuleManager.updateModuleInfo(module:getName())
ModuleManager.refreshLoadedModules()
end
end
end
function ModuleManager.reloadAllModules()
g_modules.reloadModules()
ModuleManager.refreshLoadedModules()
ModuleManager.show()
end

View File

@ -3,6 +3,12 @@ Module
description: Manage other modules
author: OTClient team
website: https://github.com/edubart/otclient
autoload: true
autoload-antecedence: 140
dependencies:
- client_topmenu
onLoad: |
dofile 'modulemanager'
ModuleManager.init()

View File

@ -3,11 +3,21 @@ ModuleListLabel < Label
background-color: alpha
text-offset: 2 0
focusable: true
color: #cccccc
$focus:
background-color: #ffffff22
color: #ffffff
$on:
background-color: #006600
$!on:
background-color: #660000
$on focus:
background-color: #004400
$!on focus:
background-color: #440000
ModuleInfoLabel < Label
$!first:
margin-top: 5
@ -19,10 +29,11 @@ ModuleValueLabel < UILabel
text-offset: 3 0
image-source: /core_styles/images/panel_flat.png
image-border: 1
height: 16
MainWindow
id: moduleManagerWindow
size: 450 450
size: 480 450
text: Module Manager
@onEscape: ModuleManager.hide()
@ -38,21 +49,31 @@ MainWindow
margin-bottom: 30
Button
id: refreshModulesButton
anchors.top: moduleList.bottom
anchors.horizontalCenter: moduleList.horizontalCenter
anchors.left: moduleList.left
margin-top: 8
width: 80
text: Refresh
@onClick: ModuleManager.refreshModules()
Button
id: reloadAllModulesButton
anchors.top: moduleList.bottom
anchors.right: moduleList.right
margin-top: 8
width: 80
text: Reload All
@onClick: ModuleManager.reloadAllModules()
Panel
id: moduleInfo
anchors.left: moduleList.right
anchors.top: parent.top
anchors.right: parent.right
margin: 0 5 5 15
layout:
type: verticalBox
fit-children: true
layout: verticalBox
height: 265
ModuleInfoLabel
text: Module name
@ -65,11 +86,6 @@ MainWindow
id: moduleDescription
height: 100
ModuleInfoLabel
text: Loaded
ModuleValueLabel
id: moduleLoaded
//ModuleInfoLabel
// text: Autoload
//ModuleValueLabel
@ -97,13 +113,13 @@ MainWindow
id: moduleVersion
Button
id: moduleLoadButton
id: moduleReloadButton
anchors.top: moduleInfo.bottom
anchors.left: moduleInfo.left
margin-top: 8
text: Load
enabled: false
@onClick: ModuleManager.loadCurrentModule()
visible: false
@onClick: ModuleManager.reloadCurrentModule()
Button
id: moduleUnloadButton
@ -112,6 +128,6 @@ MainWindow
margin-left: 10
margin-top: 8
text: Unload
enabled: false
visible: false
@onClick: ModuleManager.unloadCurrentModule()

View File

@ -3,6 +3,11 @@ Module
description: Create the options window
author: OTClient team
website: https://github.com/edubart/otclient
autoload: true
autoload-antecedence: 130
dependencies:
- client_topmenu
onLoad: |
dofile 'options'

View File

@ -45,14 +45,6 @@ function quit()
exit()
end
function reloadModule(name)
local module = g_modules.getModule(name)
if module then
module:unload()
module:load()
end
end
function autoReloadModule(name)
local function reloadEvent()
reloadModule(name)

View File

@ -86,7 +86,10 @@ end
local function doCommand()
local currentCommand = commandLineEdit:getText()
Terminal.executeCommand(currentCommand)
commandLineEdit:clearText()
if commandLineEdit then
commandLineEdit:clearText()
end
return true
end

View File

@ -1,11 +1,10 @@
Module
name: terminal
name: client_terminal
description: Terminal for executing lua functions
author: OTClient team
website: https://github.com/edubart/otclient
autoLoad: true
autoLoadAntecedence: 200
autoload: true
autoload-antecedence: 160
onLoad: |
dofile 'terminal'

View File

Before

Width:  |  Height:  |  Size: 459 B

After

Width:  |  Height:  |  Size: 459 B

View File

@ -24,6 +24,9 @@ function TopMenu.init()
TopMenu.addRightButton('logoutButton', 'Logout (Ctrl+Q)', '/core_styles/icons/logout.png', onLogout)
Keyboard.bindKeyDown('Ctrl+Q', onLogout)
connect(Game, { onLogin = TopMenu.showGameButtons,
onLogout = TopMenu.hideGameButtons })
end
function TopMenu.terminate()
@ -33,6 +36,9 @@ function TopMenu.terminate()
gameButtonsPanel = nil
topMenu:destroy()
topMenu = nil
disconnect(Game, { onLogin = TopMenu.showGameButtons,
onLogout = TopMenu.hideGameButtons })
end
function TopMenu.addButton(id, description, icon, callback, right)
@ -82,6 +88,3 @@ end
function TopMenu.getButton(id)
return topMenu:recursiveGetChildById(id)
end
connect(Game, { onLogin = TopMenu.showGameButtons,
onLogout = TopMenu.hideGameButtons })

View File

@ -3,6 +3,8 @@ Module
description: Create the top menu
author: OTClient team
website: https://github.com/edubart/otclient
autoload: true
autoload-antecedence: 120
onLoad: |
dofile 'topmenu'

View File

@ -1,17 +0,0 @@
Module
name: core
description: Contains lua classes, functions and constants used by other modules
author: OTClient team
website: https://github.com/edubart/otclient
// core must be loaded before other modules
autoLoad: true
autoLoadAntecedence: 10
// NOTE: order does matter
dependencies:
- core_lib
- core_fonts
- core_styles
- core_widgets

View File

@ -3,7 +3,8 @@ Module
description: Contains fonts used by core
author: OTClient team
website: https://github.com/edubart/otclient
canUnload: false
autoload: true
autoload-antecedence: 30
onLoad: |
importFont 'verdana-11px-antialised'

View File

@ -3,6 +3,8 @@ Module
description: Contains core lua classes, functions and constants used by other modules
author: OTClient team
website: https://github.com/edubart/otclient
autoload: true
autoload-antecedence: 10
onLoad: |
dofile 'ext/table'
@ -21,5 +23,3 @@ Module
dofile 'keyboard'
dofile 'mouse'
onUnload: |
rootWidget = nil

View File

@ -1,34 +1,20 @@
local eventId = 0
local eventList = {}
function scheduleEvent(func, delay)
if not func then return end
eventId = eventId + 1
local id = eventId
local function proxyFunc()
if eventList[id] then
if eventList[id].active then
func()
end
eventList[id] = nil
end
end
eventList[id] = { func = proxyFunc, active = true }
if delay and delay > 0 then
g_dispatcher.scheduleEvent(proxyFunc, delay)
else
g_dispatcher.addEvent(proxyFunc, false)
end
return id
local event = g_dispatcher.scheduleEvent(callback, delay)
-- must hold a reference to the callback, otherwise it would be collected
event._callback = callback
return event
end
function addEvent(func)
return scheduleEvent(func, 0)
function addEvent(callback, front)
local event = g_dispatcher.addEvent(callback, front)
-- must hold a reference to the callback, otherwise it would be collected
event._callback = callback
return event
end
function removeEvent(id)
if id and eventList[id] then
eventList[id].active = false
return true
function removeEvent(event)
if event then
event:cancel()
end
end

View File

@ -74,3 +74,16 @@ function createWidget(style, parent)
widget:setStyle(style)
return widget
end
function reloadModule(name)
local module = g_modules.getModule(name)
if module then
module:reload()
end
end
function reloadModules()
g_modules.reloadModules()
end

View File

@ -35,6 +35,28 @@ function connect(object, signalsAndSlots, pushFront)
end
end
function disconnect(object, signalsAndSlots)
for signal,slot in pairs(signalsAndSlots) do
if not object[signal] then
elseif type(object[signal]) == 'function' then
if object[signal] == slot then
object[signal] = nil
end
elseif type(object[signal]) == 'table' then
for k,func in pairs(object[signal]) do
if func == slot then
table.remove(object[signal], k)
if #object[signal] == 1 then
object[signal] = object[signal][1]
end
break
end
end
end
end
end
function extends(base)
local derived = {}
function derived.internalCreate()

View File

@ -3,6 +3,9 @@ Module
description: Contains ui styles used by other modules
author: OTClient team
website: https://github.com/edubart/otclient
reloadable: true
autoload: true
autoload-antecedence: 20
onLoad: |
importStyle 'styles/buttons.otui'
@ -20,3 +23,4 @@ Module
importStyle 'styles/popupmenus.otui'
importStyle 'styles/comboboxes.otui'
importStyle 'styles/spinboxes.otui'
importStyle 'styles/messagebox.otui'

View File

@ -0,0 +1,19 @@
MessageBoxWindow < MainWindow
id: messageBoxWindow
anchors.centerIn: parent
height: 60
width: 80
padding-bottom: 10
padding-right: 10
MessageBoxLabel < Label
id: messageBoxLabel
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
MessageBoxRightButton < Button
id: messageBoxRightButton
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 64
visible: true

View File

@ -1,20 +1,4 @@
Window < UIWindow
font: verdana-11px-antialised
size: 200 200
opacity: 1
color: white
text-offset: 0 2
text-align: top
move-policy: free
stackable: true
image-source: /core_styles/images/window.png
image-border: 4
image-border-top: 20
$disabled:
color: #aaaaaa88
Window2 < UIWindow
font: verdana-11px-antialised
size: 200 200
opacity: 1
@ -31,7 +15,7 @@ Window2 < UIWindow
$disabled:
color: #aaaaaa88
MainWindow < Window2
MainWindow < Window
anchors.centerIn: parent
MiniWindow < UIWindow

View File

@ -3,6 +3,10 @@ Module
description: Contains widgets used by other modules
author: OTClient team
website: https://github.com/edubart/otclient
reloadable: true
unloadble: false
autoload: true
autoload-antecedence: 40
onLoad: |
dofile 'uiwidget'
@ -16,6 +20,12 @@ Module
dofile 'uipopupmenu'
dofile 'uiwindow'
dofile 'uiitem'
dofile 'tooltip/tooltip'
dofile 'messagebox/messagebox'
dofile 'uimessagebox'
dofile 'tooltip'
--dofile 'messagebox/messagebox'
ToolTip.init()
onUnload: |
ToolTip.terminate()

View File

@ -1,81 +0,0 @@
MessageBox = {}
MessageBox.__index = MessageBox
-- messagebox flags
MessageBoxOk = 1
MessageBoxCancel = 2
function MessageBox.create(title, text, flags)
local box = {}
setmetatable(box, MessageBox)
-- create messagebox window
local window = displayUI('messagebox.otui')
window:lock()
window:setText(title)
local label = window:getChildById('messageBoxLabel')
label:setText(text)
label:resizeToText()
-- set window size based on label size
window:setWidth(math.max(label:getWidth() + 48, 120))
window:setHeight(label:getHeight() + 64)
-- setup messagebox first button
local buttonRight = window:getChildById('messageBoxRightButton')
if flags == MessageBoxOk then
buttonRight:setText("Ok")
box.onOk = function() end
buttonRight.onClick = function()
box.onOk()
box:destroy()
end
window.onEnter = buttonRight.onClick
window.onEscape = buttonRight.onClick
elseif flags == MessageBoxCancel then
buttonRight:setText("Cancel")
box.onCancel = function() end
buttonRight.onClick = function()
box.onCancel()
box:destroy()
end
window.onEnter = buttonRight.onClick
window.onEscape = buttonRight.onClick
end
box.window = window
return box
end
function MessageBox:destroy()
if self.onDestroy then
self.onDestroy()
self.onDestroy = nil
end
if self.window then
self.window:destroy()
self.window = nil
end
self.onOk = nil
self.onCancel = nil
end
-- shortcuts for creating message boxes
function displayMessageBox(title, text, flags)
return MessageBox.create(title, text, flags)
end
function displayErrorBox(title, text)
return MessageBox.create(title, text, MessageBoxOk)
end
function displayInfoBox(title, text)
return MessageBox.create(title, text, MessageBoxOk)
end
function displayCancelBox(title, text)
return MessageBox.create(title, text, MessageBoxCancel)
end

View File

@ -1,21 +0,0 @@
Window
id: messageBoxWindow
anchors.centerIn: parent
height: 80
width: 120
Label
id: messageBoxLabel
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
margin-top: 27
margin-bottom : 27
Button
id: messageBoxRightButton
anchors.bottom: parent.bottom
anchors.right: parent.right
margin-right: 10
margin-bottom: 10
width: 64
visible: true

View File

@ -6,6 +6,8 @@ local currentHoveredWidget
-- private functions
local function moveToolTip(tooltip)
if not tooltip:isVisible() then return end
local pos = g_window.getMousePosition()
pos.y = pos.y + 1
local xdif = g_window.getSize().width - (pos.x + tooltip:getWidth())
@ -17,28 +19,6 @@ local function moveToolTip(tooltip)
tooltip:setPosition(pos)
end
-- public functions
function ToolTip.display(text)
if text == nil then return end
ToolTip.hide()
toolTipLabel = createWidget('Label', rootWidget)
toolTipLabel:setId('toolTip')
toolTipLabel:setBackgroundColor('#111111bb')
toolTipLabel:setText(text)
toolTipLabel:resizeToText()
toolTipLabel:resize(toolTipLabel:getWidth() + 4, toolTipLabel:getHeight() + 4)
toolTipLabel.onMouseMove = moveToolTip
moveToolTip(toolTipLabel)
end
function ToolTip.hide()
if toolTipLabel then
toolTipLabel:destroy()
toolTipLabel = nil
end
end
-- UIWidget hooks
local function onWidgetHoverChange(widget, hovered)
if hovered then
if widget.tooltip then
@ -59,8 +39,39 @@ local function onWidgetStyleApply(widget, styleName, styleNode)
end
end
connect(UIWidget, { onStyleApply = onWidgetStyleApply,
onHoverChange = onWidgetHoverChange})
-- public functions
function ToolTip.init()
toolTipLabel = createWidget('Label', rootWidget)
toolTipLabel:setId('toolTip')
toolTipLabel:setBackgroundColor('#111111bb')
connect(toolTipLabel, { onMouseMove = moveToolTip })
connect(UIWidget, { onStyleApply = onWidgetStyleApply,
onHoverChange = onWidgetHoverChange})
end
function ToolTip.terminate()
disconnect(UIWidget, { onStyleApply = onWidgetStyleApply,
onHoverChange = onWidgetHoverChange })
currentHoveredWidget = nil
toolTipLabel:destroy()
toolTipLabel = nil
end
function ToolTip.display(text)
if text == nil then return end
toolTipLabel:setText(text)
toolTipLabel:resizeToText()
toolTipLabel:resize(toolTipLabel:getWidth() + 4, toolTipLabel:getHeight() + 4)
toolTipLabel:show()
toolTipLabel:raise()
moveToolTip(toolTipLabel)
end
function ToolTip.hide()
toolTipLabel:hide()
end
-- UIWidget extensions
function UIWidget:setTooltip(text)

View File

@ -1,7 +0,0 @@
Label
id: toolTipText
background-color: #111111bb
size: 200 200
id: toolTip
focusable: false
anchors.centerIn: parent

View File

@ -1,23 +1,64 @@
--[[
UIMessageBox = extends(UIWindow)
function UIMessageBox.create(title, message)
MessageBoxOk = 1
MessageBoxCancel = 2
-- messagebox cannot be created from otui files
UIMessageBox.create = nil
function UIMessageBox.display(title, message, flags)
local messagebox = UIMessageBox.internalCreate()
rootWidget:addChild(messagebox)
messagebox:setStyle('MessageBoxWindow')
messagebox:setText(title)
local messageLabel = self:getChildById('messageLabel')
label:setText(message)
label:resizeToText()
window:setWidth(math.max(label:getWidth() + self:getPaddingLeft() + self:getPaddingRight(), self:getWidth()))
window:setHeight(label:getHeight() + self:getPaddingTop() + self:getPaddingBottom())
local messageLabel = createWidget('MessageBoxLabel', messagebox)
messageLabel:setText(message)
messageLabel:resizeToText()
messagebox:setWidth(math.max(messageLabel:getWidth() + 48, messagebox:getWidth()))
messagebox:setHeight(math.max(messageLabel:getHeight() + 64, messagebox:getHeight()))
-- setup messagebox first button
local buttonRight = createWidget('MessageBoxRightButton', messagebox)
if flags == MessageBoxOk then
buttonRight:setText('Ok')
connect(buttonRight, { onClick = function(self) self:getParent():ok() end })
elseif flags == MessageBoxCancel then
buttonRight:setText('Cancel')
connect(buttonRight, { onClick = function(self) self:getParent():cancel() end })
end
connect(messagebox, { onEnter = function(self) self:destroy() end })
connect(messagebox, { onEscape = function(self) self:destroy() end })
messagebox:lock()
return messagebox
end
function UIMessageBox:setTitle(title)
function displayInfoBox(title, message)
return UIMessageBox.display(title, message, MessageBoxOk)
end
function UIMessageBox:setMessage(message)
function displayErrorBox(title, message)
return UIMessageBox.display(title, message, MessageBoxOk)
end
function displayCancelBox(title, message)
return UIMessageBox.display(title, message, MessageBoxCancel)
end
function UIMessageBox:ok()
signalcall(self.onOk, self)
self.onOk = nil
self:destroy()
end
function UIMessageBox:cancel()
signalcall(self.onCancel, self)
self.onCancel = nil
self:destroy()
end
]]--

View File

@ -90,13 +90,13 @@ end
function Game.onLoginError(message)
CharacterList.destroyLoadBox()
local errorBox = displayErrorBox("Login Error", "Login error: " .. message)
errorBox.onOk = CharacterList.show
connect(errorBox, { onOk = CharacterList.show })
end
function Game.onConnectionError(message)
CharacterList.destroyLoadBox()
local errorBox = displayErrorBox("Login Error", "Connection error: " .. message)
errorBox.onOk = CharacterList.show
connect(errorBox, { onOk = CharacterList.show })
end
local function onApplicationClose()

View File

@ -30,5 +30,4 @@ UIGame
id: mouseGrabber
focusable: false
visible: false
phantom: true

View File

@ -33,7 +33,7 @@ public:
Event(const SimpleCallback& callback) : m_callback(callback), m_canceled(false), m_executed(false) { }
void execute() {
if(!m_canceled && !m_executed) {
if(!m_canceled && !m_executed && m_callback) {
m_callback();
m_executed = true;
}

View File

@ -52,8 +52,10 @@ bool Module::load()
if(m_loadCallback)
m_loadCallback();
logInfo("Loaded module '", m_name, "'");
m_loaded = true;
logInfo("Loaded module '", m_name, "'");
g_modules.updateModuleLoadOrder(asModule());
return true;
}
@ -63,9 +65,33 @@ void Module::unload()
if(m_unloadCallback)
m_unloadCallback();
m_loaded = false;
logInfo("Unloaded module '", m_name, "'");
g_modules.updateModuleLoadOrder(asModule());
}
}
bool Module::reload()
{
unload();
return load();
}
bool Module::isDependent()
{
for(const ModulePtr& module : g_modules.getModules()) {
if(module->isLoaded() && module->hasDependency(m_name))
return true;
}
return false;
}
bool Module::hasDependency(const std::string& name)
{
if(std::find(m_dependencies.begin(), m_dependencies.end(), name) != m_dependencies.end())
return true;
return false;
}
void Module::discover(const OTMLNodePtr& moduleNode)
{
const static std::string none = "none";
@ -73,8 +99,9 @@ void Module::discover(const OTMLNodePtr& moduleNode)
m_author = moduleNode->valueAt("author", none);
m_website = moduleNode->valueAt("website", none);
m_version = moduleNode->valueAt("version", none);
m_autoLoad = moduleNode->valueAt<bool>("autoLoad", false);
m_autoLoadAntecedence = moduleNode->valueAt<int>("autoLoadAntecedence", 100);
m_autoLoad = moduleNode->valueAt<bool>("autoload", false);
m_unloadable = moduleNode->valueAt<bool>("unloadable", true);
m_autoLoadAntecedence = moduleNode->valueAt<int>("autoload-antecedence", 9999);
if(OTMLNodePtr node = moduleNode->get("dependencies")) {
for(const OTMLNodePtr& tmp : node->children())
@ -95,4 +122,3 @@ void Module::discover(const OTMLNodePtr& moduleNode)
m_unloadCallback = g_lua.polymorphicPop<SimpleCallback>();
}
}

View File

@ -35,8 +35,12 @@ public:
bool load();
void unload();
bool reload();
bool canUnload() { return m_loaded && m_unloadable && !isDependent(); }
bool isLoaded() { return m_loaded; }
bool isDependent();
bool hasDependency(const std::string& name);
std::string getDescription() { return m_description; }
std::string getName() { return m_name; }
@ -46,6 +50,8 @@ public:
bool isAutoLoad() { return m_autoLoad; }
int getAutoLoadAntecedence() { return m_autoLoadAntecedence; }
ModulePtr asModule() { return std::static_pointer_cast<Module>(shared_from_this()); }
protected:
void discover(const OTMLNodePtr& moduleNode);
friend class ModuleManager;
@ -53,6 +59,7 @@ protected:
private:
Boolean<false> m_loaded;
Boolean<false> m_autoLoad;
Boolean<true> m_unloadable;
int m_autoLoadAntecedence;
std::string m_name;
std::string m_description;

View File

@ -61,13 +61,13 @@ void ModuleManager::autoLoadModules(int maxAntecedence)
void ModuleManager::discoverModulesPath()
{
// search for modules directory
std::string possibleDirs[] = { "modules",
g_resources.getBaseDir() + "modules",
g_resources.getBaseDir() + "../modules",
g_resources.getBaseDir() + "../share/" + g_app->getAppName() + "/modules",
"" };
std::string possibleModulesDirs[] = { "modules",
g_resources.getBaseDir() + "modules",
g_resources.getBaseDir() + "../modules",
g_resources.getBaseDir() + "../share/" + g_app->getAppName() + "/modules",
"" };
bool found = false;
for(const std::string& dir : possibleDirs) {
for(const std::string& dir : possibleModulesDirs) {
// try to add module directory
if(g_resources.addToSearchPath(dir, false)) {
logInfo("Using modules directory '", dir.c_str(), "'");
@ -78,6 +78,21 @@ void ModuleManager::discoverModulesPath()
if(!found)
logFatal("Could not find modules directory");
// search for addons directory
std::string possibleAddonsDirs[] = { "addons",
g_resources.getBaseDir() + "addons",
g_resources.getBaseDir() + "../addons",
g_resources.getBaseDir() + "../addons/" + g_app->getAppName() + "/modules",
"" };
for(const std::string& dir : possibleAddonsDirs) {
// try to add module directory
if(g_resources.addToSearchPath(dir, false)) {
logInfo("Using addons directory '", dir.c_str(), "'");
found = true;
break;
}
}
}
ModulePtr ModuleManager::discoverModule(const std::string& moduleFile)
@ -99,6 +114,7 @@ ModulePtr ModuleManager::discoverModule(const std::string& moduleFile)
}
module->discover(moduleNode);
// not loaded modules are always in back
if(push)
m_modules.push_back(module);
} catch(Exception& e) {
@ -116,10 +132,30 @@ void ModuleManager::ensureModuleLoaded(const std::string& moduleName)
void ModuleManager::unloadModules()
{
for(const ModulePtr& module : m_modules)
auto modulesBackup = m_modules;
for(const ModulePtr& module : modulesBackup)
module->unload();
}
void ModuleManager::reloadModules()
{
std::deque<ModulePtr> toLoadList;
// unload in the reverse direction, try to unload upto 10 times (because of dependencies)
for(int i=0;i<10;++i) {
auto modulesBackup = m_modules;
for(const ModulePtr& module : modulesBackup) {
if(module->isLoaded() && module->canUnload()) {
module->unload();
toLoadList.push_front(module);
}
}
}
for(const ModulePtr& module : toLoadList)
module->load();
}
ModulePtr ModuleManager::getModule(const std::string& moduleName)
{
for(const ModulePtr& module : m_modules)
@ -127,3 +163,14 @@ ModulePtr ModuleManager::getModule(const std::string& moduleName)
return module;
return nullptr;
}
void ModuleManager::updateModuleLoadOrder(ModulePtr module)
{
auto it = std::find(m_modules.begin(), m_modules.end(), module);
if(it != m_modules.end())
m_modules.erase(it);
if(module->isLoaded())
m_modules.push_front(module);
else
m_modules.push_back(module);
}

View File

@ -34,12 +34,18 @@ public:
ModulePtr discoverModule(const std::string& moduleFile);
void ensureModuleLoaded(const std::string& moduleName);
void unloadModules();
void reloadModules();
ModulePtr getModule(const std::string& moduleName);
std::vector<ModulePtr> getModules() { return m_modules; }
std::deque<ModulePtr> getModules() { return m_modules; }
protected:
void updateModuleLoadOrder(ModulePtr module);
friend class Module;
private:
std::vector<ModulePtr> m_modules;
std::deque<ModulePtr> m_modules;
std::multimap<int, ModulePtr> m_autoLoadModules;
};

View File

@ -48,8 +48,16 @@ bool FontManager::importFont(std::string fontFile)
OTMLNodePtr fontNode = doc->at("Font");
std::string name = fontNode->valueAt("name");
if(fontExists(name))
Fw::throwException("font '", name, "' already exists, cannot have duplicate font names");
//if(fontExists(name))
// Fw::throwException("font '", name, "' already exists, cannot have duplicate font names");
// remove any font with the same name
for(auto it = m_fonts.begin(); it != m_fonts.end(); ++it) {
if((*it)->getName() == name) {
m_fonts.erase(it);
break;
}
}
FontPtr font(new Font(name));
font->load(fontNode);

View File

@ -47,11 +47,15 @@ void Application::registerLuaFunctions()
// Event
g_lua.registerClass<Event>();
g_lua.bindClassMemberFunction<Event>("cancel", &Event::cancel);
g_lua.bindClassMemberFunction<Event>("execute", &Event::execute);
g_lua.bindClassMemberFunction<Event>("isCanceled", &Event::isCanceled);
g_lua.bindClassMemberFunction<Event>("isExecuted", &Event::isExecuted);
// ScheduledEvent
g_lua.registerClass<ScheduledEvent, Event>();
g_lua.bindClassMemberFunction<ScheduledEvent>("reamaningTicks", &ScheduledEvent::reamaningTicks);
g_lua.bindClassMemberFunction<ScheduledEvent>("ticks", &ScheduledEvent::ticks);
// UIWidget
g_lua.registerClass<UIWidget>();
@ -370,6 +374,8 @@ void Application::registerLuaFunctions()
g_lua.registerClass<Module>();
g_lua.bindClassMemberFunction<Module>("load", &Module::load);
g_lua.bindClassMemberFunction<Module>("unload", &Module::unload);
g_lua.bindClassMemberFunction<Module>("reload", &Module::reload);
g_lua.bindClassMemberFunction<Module>("canUnload", &Module::canUnload);
g_lua.bindClassMemberFunction<Module>("isLoaded", &Module::isLoaded);
g_lua.bindClassMemberFunction<Module>("getDescription", &Module::getDescription);
g_lua.bindClassMemberFunction<Module>("getName", &Module::getName);
@ -397,6 +403,8 @@ void Application::registerLuaFunctions()
// Application
g_lua.registerStaticClass("g_app");
g_lua.bindClassStaticFunction("g_app", "exit", std::bind(&Application::exit, g_app));
g_lua.bindClassStaticFunction("g_app", "isRunning", std::bind(&Application::isRunning, g_app));
g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app));
// ConfigManager
g_lua.registerStaticClass("g_configs");
@ -477,6 +485,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassStaticFunction("g_modules", "discoverModule", std::bind(&ModuleManager::discoverModule, &g_modules, _1));
g_lua.bindClassStaticFunction("g_modules", "ensureModuleLoaded", std::bind(&ModuleManager::ensureModuleLoaded, &g_modules, _1));
g_lua.bindClassStaticFunction("g_modules", "unloadModules", std::bind(&ModuleManager::unloadModules, &g_modules));
g_lua.bindClassStaticFunction("g_modules", "reloadModules", std::bind(&ModuleManager::reloadModules, &g_modules));
g_lua.bindClassStaticFunction("g_modules", "getModule", std::bind(&ModuleManager::getModule, &g_modules, _1));
g_lua.bindClassStaticFunction("g_modules", "getModules", std::bind(&ModuleManager::getModules, &g_modules));

View File

@ -132,6 +132,9 @@ bool luavalue_cast(int index, std::vector<T>& vec);
template<class T>
void push_luavalue(const std::deque<T>& vec);
template<typename T>
bool luavalue_cast(int index, std::deque<T>& vec);
// tuple
template<typename... Args>
void push_luavalue(const std::tuple<Args...>& tuple);
@ -279,6 +282,22 @@ void push_luavalue(const std::deque<T>& vec) {
}
}
template<typename T>
bool luavalue_cast(int index, std::deque<T>& vec)
{
if(g_lua.isTable(index)) {
g_lua.pushNil();
while(g_lua.next(index < 0 ? index-1 : index)) {
T value;
if(luavalue_cast(-1, value))
vec.push_back(value);
g_lua.pop();
}
return true;
}
return false;
}
template<int N>
struct push_tuple_luavalue {
template<typename Tuple>

View File

@ -554,15 +554,17 @@ void UIWidget::destroy()
#ifdef DEBUG
auto self = asUIWidget();
g_lua.collectGarbage();
g_dispatcher.addEvent([self] {
if(self != g_ui.getRootWidget()) {
g_lua.collectGarbage();
g_dispatcher.addEvent([self] {
g_lua.collectGarbage();
if(self->getUseCount() != 1)
logWarning("widget '", self->getId(), "' destroyed but still have ", self->getUseCount()-1, " reference(s) left");
g_dispatcher.addEvent([self] {
g_lua.collectGarbage();
if(self->getUseCount() != 1)
logWarning("widget '", self->getId(), "' destroyed but still have ", self->getUseCount()-1, " reference(s) left");
});
});
});
}
#endif
m_destroyed = true;
@ -853,9 +855,9 @@ UIWidgetPtr UIWidget::getChildById(const std::string& childId)
UIWidgetPtr UIWidget::getChildByPos(const Point& childPos)
{
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
const UIWidgetPtr& widget = (*it);
if(widget->isExplicitlyVisible() && widget->containsPoint(childPos))
return widget;
const UIWidgetPtr& child = (*it);
if(child->isExplicitlyVisible() && child->containsPoint(childPos))
return child;
}
return nullptr;
@ -884,7 +886,8 @@ UIWidgetPtr UIWidget::recursiveGetChildById(const std::string& id)
UIWidgetPtr UIWidget::recursiveGetChildByPos(const Point& childPos)
{
for(const UIWidgetPtr& child : m_children) {
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
const UIWidgetPtr& child = (*it);
if(child->isExplicitlyVisible() && child->containsPoint(childPos)) {
if(UIWidgetPtr subChild = child->recursiveGetChildByPos(childPos))
return subChild;
@ -1280,9 +1283,9 @@ bool UIWidget::propagateOnKeyPress(uchar keyCode, int keyboardModifiers, int aut
return true;
}
if(autoRepeatTicks == 0 || autoRepeatTicks >= m_autoRepeatDelay) {
if(autoRepeatTicks == 0 || autoRepeatTicks >= m_autoRepeatDelay)
return onKeyPress(keyCode, keyboardModifiers, autoRepeatTicks);
} else
else
return false;
}

View File

@ -86,7 +86,7 @@ void UIWidget::onFontChange(const std::string& font)
void UIWidget::wrapText()
{
setText(m_font->wrapText(m_text, getWidth()));
setText(m_font->wrapText(m_text, getWidth() - m_textOffset.x));
}
void UIWidget::setText(const std::string& text)

View File

@ -37,11 +37,18 @@ void OTClient::init(const std::vector<std::string>& args)
Application::init(args, Fw::AppEnableAll);
g_modules.discoverModules();
g_modules.autoLoadModules(100);
g_modules.ensureModuleLoaded("client");
g_modules.autoLoadModules(1000);
//g_map.load();
// core modules 0-99
g_modules.autoLoadModules(99);
g_modules.ensureModuleLoaded("core_lib");
// client modules 100-499
g_modules.autoLoadModules(499);
g_modules.ensureModuleLoaded("client_main");
// game modules 500-999
g_modules.autoLoadModules(999);
g_modules.ensureModuleLoaded("game");
// addons 1000-9999
g_modules.autoLoadModules(9999);
// load otclientrc.lua
if(g_resources.fileExists("/otclientrc.lua")) {