Protocol 10.72 (Authenticator) Support, Unjustified Points diplay

- Unjustified Points (Better topbar icon would be nice)
![Unjustified Points](http://i.gyazo.com/81286f46d9b4d56b3fe864140173cf34.png)
- Authenticator token support
- adjusted 'can change pvp frame' to 1054
- ...
This commit is contained in:
TheSumm 2015-01-18 15:14:07 +01:00
parent 24b1526534
commit ddec9627b8
25 changed files with 525 additions and 31 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

View File

@ -27,7 +27,7 @@ local function tryLogin(charInfo, tries)
CharacterList.hide() CharacterList.hide()
g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName) g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName, G.authenticatorToken)
loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to game server...')) loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to game server...'))
connect(loadBox, { onCancel = function() connect(loadBox, { onCancel = function()
@ -109,6 +109,16 @@ function onGameLoginError(message)
end end
end end
function onGameLoginToken(unknown)
CharacterList.destroyLoadBox()
-- TODO: make it possible to enter a new token here / prompt token
errorBox = displayErrorBox(tr("Two-Factor Authentification"), 'A new authentification token is required.\nPlease login again.')
errorBox.onOk = function()
errorBox = nil
EnterGame.show()
end
end
function onGameConnectionError(message, code) function onGameConnectionError(message, code)
CharacterList.destroyLoadBox() CharacterList.destroyLoadBox()
local text = translateNetworkError(code, g_game.getProtocolGame() and g_game.getProtocolGame():isConnecting(), message) local text = translateNetworkError(code, g_game.getProtocolGame() and g_game.getProtocolGame():isConnecting(), message)
@ -131,6 +141,7 @@ end
-- public functions -- public functions
function CharacterList.init() function CharacterList.init()
connect(g_game, { onLoginError = onGameLoginError }) connect(g_game, { onLoginError = onGameLoginError })
connect(g_game, { onLoginToken = onGameLoginToken })
connect(g_game, { onUpdateNeeded = onGameUpdateNeeded }) connect(g_game, { onUpdateNeeded = onGameUpdateNeeded })
connect(g_game, { onConnectionError = onGameConnectionError }) connect(g_game, { onConnectionError = onGameConnectionError })
connect(g_game, { onGameStart = CharacterList.destroyLoadBox }) connect(g_game, { onGameStart = CharacterList.destroyLoadBox })
@ -144,6 +155,7 @@ end
function CharacterList.terminate() function CharacterList.terminate()
disconnect(g_game, { onLoginError = onGameLoginError }) disconnect(g_game, { onLoginError = onGameLoginError })
disconnect(g_game, { onLoginToken = onGameLoginToken })
disconnect(g_game, { onUpdateNeeded = onGameUpdateNeeded }) disconnect(g_game, { onUpdateNeeded = onGameUpdateNeeded })
disconnect(g_game, { onConnectionError = onGameConnectionError }) disconnect(g_game, { onConnectionError = onGameConnectionError })
disconnect(g_game, { onGameStart = CharacterList.destroyLoadBox }) disconnect(g_game, { onGameStart = CharacterList.destroyLoadBox })

View File

@ -16,7 +16,7 @@ CharacterWidget < UIWidget
Label Label
id: name id: name
color: #aaaaaa color: #bbbbbb
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
font: verdana-11px-monochrome font: verdana-11px-monochrome
@ -29,8 +29,7 @@ CharacterWidget < UIWidget
Label Label
id: worldName id: worldName
color: #ffffff color: #bbbbbb
color: #aaaaaa
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
margin-right: 5 margin-right: 5
@ -59,6 +58,7 @@ MainWindow
TextList TextList
id: characters id: characters
background-color: #565656
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: characterListScrollBar.left anchors.right: characterListScrollBar.left

View File

@ -111,7 +111,7 @@ function EnterGame.init()
local port = g_settings.get('port') local port = g_settings.get('port')
local autologin = g_settings.getBoolean('autologin') local autologin = g_settings.getBoolean('autologin')
local clientVersion = g_settings.getInteger('client-version') local clientVersion = g_settings.getInteger('client-version')
if clientVersion == 0 then clientVersion = 1071 end if clientVersion == 0 then clientVersion = 1072 end
if port == nil or port == 0 then port = 7171 end if port == nil or port == 0 then port = 7171 end
@ -122,7 +122,10 @@ function EnterGame.init()
enterGame:getChildById('serverPortTextEdit'):setText(port) enterGame:getChildById('serverPortTextEdit'):setText(port)
enterGame:getChildById('autoLoginBox'):setChecked(autologin) enterGame:getChildById('autoLoginBox'):setChecked(autologin)
clientBox = enterGame:getChildById('clientComboBox') clientBox = enterGame:getChildById('clientComboBox')
connect(clientBox, { onOptionChange = EnterGame.onClientVersionChange })
for _, proto in pairs(g_game.getSupportedClients()) do for _, proto in pairs(g_game.getSupportedClients()) do
clientBox:addOption(proto) clientBox:addOption(proto)
end end
@ -152,6 +155,7 @@ end
function EnterGame.terminate() function EnterGame.terminate()
g_keyboard.unbindKeyDown('Ctrl+G') g_keyboard.unbindKeyDown('Ctrl+G')
disconnect(clientBox, { onOptionChange = EnterGame.onClientVersionChange })
enterGame:destroy() enterGame:destroy()
enterGame = nil enterGame = nil
enterGameButton:destroy() enterGameButton:destroy()
@ -210,14 +214,55 @@ end
function EnterGame.clearAccountFields() function EnterGame.clearAccountFields()
enterGame:getChildById('accountNameTextEdit'):clearText() enterGame:getChildById('accountNameTextEdit'):clearText()
enterGame:getChildById('accountPasswordTextEdit'):clearText() enterGame:getChildById('accountPasswordTextEdit'):clearText()
enterGame:getChildById('authenticatorTokenTextEdit'):clearText()
enterGame:getChildById('accountNameTextEdit'):focus() enterGame:getChildById('accountNameTextEdit'):focus()
g_settings.remove('account') g_settings.remove('account')
g_settings.remove('password') g_settings.remove('password')
end end
function EnterGame.toggleAuthenticatorToken(enabled)
if enabled == enterGame.authenticatorEnabled then
return
end
if enabled then
enterGame:getChildById('authenticatorTokenLabel'):setVisible(true)
enterGame:getChildById('authenticatorTokenTextEdit'):setVisible(true)
local serverLabel = enterGame:getChildById('serverLabel')
serverLabel:setMarginTop(serverLabel:getMarginTop() + enterGame.authenticatorHeight)
enterGame:breakAnchors()
enterGame:setY(enterGame:getY() - enterGame.authenticatorHeight)
enterGame:bindRectToParent()
enterGame:setHeight(enterGame:getHeight() + enterGame.authenticatorHeight)
else
enterGame:getChildById('authenticatorTokenLabel'):setVisible(false)
enterGame:getChildById('authenticatorTokenTextEdit'):setVisible(false)
local serverLabel = enterGame:getChildById('serverLabel')
serverLabel:setMarginTop(serverLabel:getMarginTop() - enterGame.authenticatorHeight)
enterGame:breakAnchors()
enterGame:setY(enterGame:getY() + enterGame.authenticatorHeight)
enterGame:bindRectToParent()
enterGame:setHeight(enterGame:getHeight() - enterGame.authenticatorHeight)
end
enterGame.authenticatorEnabled = enabled
end
function EnterGame.onClientVersionChange(comboBox, text, data)
local clientVersion = tonumber(text)
EnterGame.toggleAuthenticatorToken(clientVersion >= 1072)
end
function EnterGame.doLogin() function EnterGame.doLogin()
G.account = enterGame:getChildById('accountNameTextEdit'):getText() G.account = enterGame:getChildById('accountNameTextEdit'):getText()
G.password = enterGame:getChildById('accountPasswordTextEdit'):getText() G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
G.authenticatorToken = enterGame:getChildById('authenticatorTokenTextEdit'):getText()
G.host = enterGame:getChildById('serverHostTextEdit'):getText() G.host = enterGame:getChildById('serverHostTextEdit'):getText()
G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText()) G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText())
local clientVersion = tonumber(clientBox:getText()) local clientVersion = tonumber(clientBox:getText())
@ -251,7 +296,7 @@ function EnterGame.doLogin()
g_game.chooseRsa(G.host) g_game.chooseRsa(G.host)
if modules.game_things.isLoaded() then if modules.game_things.isLoaded() then
protocolLogin:login(G.host, G.port, G.account, G.password) protocolLogin:login(G.host, G.port, G.account, G.password, G.authenticatorToken)
else else
loadBox:destroy() loadBox:destroy()
loadBox = nil loadBox = nil
@ -272,6 +317,7 @@ function EnterGame.setDefaultServer(host, port, protocol)
local clientLabel = enterGame:getChildById('clientLabel') local clientLabel = enterGame:getChildById('clientLabel')
local accountTextEdit = enterGame:getChildById('accountNameTextEdit') local accountTextEdit = enterGame:getChildById('accountNameTextEdit')
local passwordTextEdit = enterGame:getChildById('accountPasswordTextEdit') local passwordTextEdit = enterGame:getChildById('accountPasswordTextEdit')
local authenticatorTokenTextEdit = enterGame:getChildById('authenticatorTokenTextEdit')
if hostTextEdit:getText() ~= host then if hostTextEdit:getText() ~= host then
hostTextEdit:setText(host) hostTextEdit:setText(host)
@ -279,6 +325,7 @@ function EnterGame.setDefaultServer(host, port, protocol)
clientBox:setCurrentOption(protocol) clientBox:setCurrentOption(protocol)
accountTextEdit:setText('') accountTextEdit:setText('')
passwordTextEdit:setText('') passwordTextEdit:setText('')
authenticatorTokenTextEdit:setText('')
end end
end end
@ -291,6 +338,13 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig
portTextEdit:setText(port) portTextEdit:setText(port)
portTextEdit:setVisible(false) portTextEdit:setVisible(false)
portTextEdit:setHeight(0) portTextEdit:setHeight(0)
local authenticatorTokenTextEdit = enterGame:getChildById('authenticatorTokenTextEdit')
authenticatorTokenTextEdit:setText('')
authenticatorTokenTextEdit:setVisible(false)
authenticatorTokenTextEdit:setHeight(0)
local authenticatorTokenLabel = enterGame:getChildById('authenticatorTokenLabel')
authenticatorTokenLabel:setVisible(false)
authenticatorTokenLabel:setHeight(0)
clientBox:setCurrentOption(protocol) clientBox:setCurrentOption(protocol)
clientBox:setVisible(false) clientBox:setVisible(false)
@ -312,7 +366,7 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig
serverListButton:setWidth(0) serverListButton:setWidth(0)
local rememberPasswordBox = enterGame:getChildById('rememberPasswordBox') local rememberPasswordBox = enterGame:getChildById('rememberPasswordBox')
rememberPasswordBox:setMarginTop(-5) rememberPasswordBox:setMarginTop(-14)
if not windowWidth then windowWidth = 236 end if not windowWidth then windowWidth = 236 end
enterGame:setWidth(windowWidth) enterGame:setWidth(windowWidth)

View File

@ -21,6 +21,8 @@ ServerListButton < UIButton
EnterGameWindow EnterGameWindow
id: enterGame id: enterGame
&authenticatorEnabled: false
&authenticatorHeight: 44
@onEnter: EnterGame.doLogin() @onEnter: EnterGame.doLogin()
MenuLabel MenuLabel
@ -50,12 +52,30 @@ EnterGameWindow
anchors.top: prev.bottom anchors.top: prev.bottom
margin-top: 2 margin-top: 2
MenuLabel
id: authenticatorTokenLabel
!text: tr('Authenticator Token')
anchors.left: prev.left
anchors.top: prev.bottom
text-auto-resize: true
margin-top: 8
visible: false
TextEdit
id: authenticatorTokenTextEdit
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 2
visible: false
max-length: 8
MenuLabel MenuLabel
id: serverLabel id: serverLabel
!text: tr('Server') !text: tr('Server')
anchors.left: prev.left anchors.left: prev.left
anchors.top: prev.bottom anchors.top: prev.bottom
margin-top: 8 margin-top: -36
text-auto-resize: true text-auto-resize: true
ServerListButton ServerListButton

View File

@ -30,5 +30,6 @@ Module
- game_spelllist - game_spelllist
- game_cooldown - game_cooldown
- game_modaldialog - game_modaldialog
- game_unjustifiedpoints
@onLoad: init() @onLoad: init()
@onUnload: terminate() @onUnload: terminate()

View File

@ -0,0 +1,135 @@
unjustifiedPointsWindow = nil
unjustifiedPointsButton = nil
contentsPanel = nil
openPvpSituationsLabel = nil
currentSkullWidget = nil
skullTimeLabel = nil
dayProgressBar = nil
weekProgressBar = nil
monthProgressBar = nil
daySkullWidget = nil
weekSkullWidget = nil
monthSkullWidget = nil
function init()
connect(g_game, { onGameStart = online,
onUnjustifiedPointsChange = onUnjustifiedPointsChange,
onOpenPvpSituationsChange = onOpenPvpSituationsChange })
connect(LocalPlayer, { onSkullChange = onSkullChange } )
unjustifiedPointsButton = modules.client_topmenu.addRightGameToggleButton('unjustifiedPointsButton',
tr('Unjustified Points'), '/images/topbuttons/unjustifiedpoints', toggle)
unjustifiedPointsButton:setOn(true)
unjustifiedPointsButton:hide()
unjustifiedPointsWindow = g_ui.loadUI('unjustifiedpoints', modules.game_interface.getRightPanel())
unjustifiedPointsWindow:disableResize()
unjustifiedPointsWindow:setup()
contentsPanel = unjustifiedPointsWindow:getChildById('contentsPanel')
openPvpSituationsLabel = contentsPanel:getChildById('openPvpSituationsLabel')
currentSkullWidget = contentsPanel:getChildById('currentSkullWidget')
skullTimeLabel = contentsPanel:getChildById('skullTimeLabel')
dayProgressBar = contentsPanel:getChildById('dayProgressBar')
weekProgressBar = contentsPanel:getChildById('weekProgressBar')
monthProgressBar = contentsPanel:getChildById('monthProgressBar')
daySkullWidget = contentsPanel:getChildById('daySkullWidget')
weekSkullWidget = contentsPanel:getChildById('weekSkullWidget')
monthSkullWidget = contentsPanel:getChildById('monthSkullWidget')
if g_game.isOnline() then
online()
end
end
function terminate()
disconnect(g_game, { onGameStart = online,
onUnjustifiedPointsChange = onUnjustifiedPointsChange,
onOpenPvpSituationsChange = onOpenPvpSituationsChange })
disconnect(LocalPlayer, { onSkullChange = onSkullChange } )
unjustifiedPointsWindow:destroy()
unjustifiedPointsButton:destroy()
end
function onMiniWindowClose()
unjustifiedPointsButton:setOn(false)
end
function toggle()
if unjustifiedPointsButton:isOn() then
unjustifiedPointsWindow:close()
unjustifiedPointsButton:setOn(false)
else
unjustifiedPointsWindow:open()
unjustifiedPointsButton:setOn(true)
end
end
function online()
if g_game.getFeature(GameUnjustifiedPoints) then
unjustifiedPointsButton:show()
else
unjustifiedPointsButton:hide()
unjustifiedPointsWindow:close()
end
refresh()
end
function refresh()
local localPlayer = g_game.getLocalPlayer()
local unjustifiedPoints = g_game.getUnjustifiedPoints()
onUnjustifiedPointsChange(unjustifiedPoints)
onSkullChange(localPlayer, localPlayer:getSkull())
onOpenPvpSituationsChange(g_game.getOpenPvpSituations())
end
function onSkullChange(localPlayer, skull)
if not localPlayer:isLocalPlayer() then return end
if skull == SkullRed or skull == SkullBlack then
currentSkullWidget:setIcon(getSkullImagePath(skull))
currentSkullWidget:setTooltip('Remaining skull time')
else
currentSkullWidget:setIcon('')
currentSkullWidget:setTooltip('You have no skull')
end
daySkullWidget:setIcon(getSkullImagePath(getNextSkullId(skull)))
weekSkullWidget:setIcon(getSkullImagePath(getNextSkullId(skull)))
monthSkullWidget:setIcon(getSkullImagePath(getNextSkullId(skull)))
end
function onOpenPvpSituationsChange(amount)
openPvpSituationsLabel:setText(amount)
end
function onUnjustifiedPointsChange(unjustifiedPoints)
if unjustifiedPoints.skullTime == 0 then
skullTimeLabel:setText('No skull')
skullTimeLabel:setTooltip('You have no skull')
else
skullTimeLabel:setText(unjustifiedPoints.skullTime .. ' days')
skullTimeLabel:setTooltip('Remaining skull time')
end
dayProgressBar:setValue(unjustifiedPoints.killsDay, 0, 100)
dayProgressBar:setTooltip(string.format('Unjustified points gained during the last 24 hours.\n%i kills left.', unjustifiedPoints.killsDayRemaining))
dayProgressBar:setText(unjustifiedPoints.killsDayRemaining .. ' kills left')
weekProgressBar:setValue(unjustifiedPoints.killsWeek, 0, 100)
weekProgressBar:setTooltip(string.format('Unjustified points gained during the last 7 days.\n%i kills left.', unjustifiedPoints.killsWeekRemaining))
weekProgressBar:setText(unjustifiedPoints.killsWeekRemaining .. ' kills left')
monthProgressBar:setValue(unjustifiedPoints.killsMonth, 0, 100)
monthProgressBar:setTooltip(string.format('Unjustified points gained during the last 30 days.\n%i kills left.', unjustifiedPoints.killsMonthRemaining))
monthProgressBar:setText(unjustifiedPoints.killsMonthRemaining .. ' kills left')
end

View File

@ -0,0 +1,8 @@
Module
name: game_unjustifiedpoints
description: View unjustified points
author: Summ
sandboxed: true
scripts: [ unjustifiedpoints ]
@onLoad: init()
@onUnload: terminate()

View File

@ -0,0 +1,80 @@
SkullProgressBar < ProgressBar
height: 13
margin: 4 18 0 10
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
SkullWidget < UIWidget
size: 13 13
margin-right: 2
anchors.right: parent.right
image-source: /images/game/skull_socket
MiniWindow
id: unjustifiedPointsWindow
!text: tr('Unjustified Points')
height: 114
icon: /images/topbuttons/unjustifiedpoints
@onClose: modules.game_unjustifiedpoints.onMiniWindowClose()
&save: true
MiniWindowContents
Label
anchors.top: parent.top
anchors.left: parent.left
!text: tr('Open PvP')
!tooltip: tr('Open PvP Situations')
phantom: false
margin-top: 2
margin-left: 10
Label
id: openPvpSituationsLabel
anchors.top: prev.bottom
anchors.left: parent.left
font: verdana-11px-rounded
margin-left: 12
phantom: false
Label
anchors.top: parent.top
anchors.right: parent.right
!text: tr('Skull Time')
margin-top: 2
margin-right: 10
SkullWidget
id: currentSkullWidget
anchors.top: prev.bottom
margin-right: 10
Label
id: skullTimeLabel
anchors.top: prev.top
anchors.right: prev.left
font: verdana-11px-rounded
margin-right: 6
phantom: false
SkullProgressBar
id: dayProgressBar
margin-top: 10
SkullWidget
id: daySkullWidget
anchors.top: prev.top
SkullProgressBar
id: weekProgressBar
SkullWidget
id: weekSkullWidget
anchors.top: prev.top
SkullProgressBar
id: monthProgressBar
SkullWidget
id: monthSkullWidget
anchors.top: prev.top

View File

@ -127,6 +127,8 @@ GameLoginPacketEncryption = 63
GameClientVersion = 64 GameClientVersion = 64
GameContentRevision = 65 GameContentRevision = 65
GameExperienceBonus = 66 GameExperienceBonus = 66
GameAuthenticator = 67
GameUnjustifiedPoints = 68
TextColors = { TextColors = {
red = '#f55e5e', --'#c83200' red = '#f55e5e', --'#c83200'

View File

@ -35,6 +35,13 @@ NpcIconTradeQuest = 4
-- @} -- @}
function getNextSkullId(skullId)
if skullId == SkullRed or skullId == SkullBlack then
return SkullBlack
end
return SkullRed
end
function getSkullImagePath(skullId) function getSkullImagePath(skullId)
local path local path
if skullId == SkullYellow then if skullId == SkullYellow then

View File

@ -74,7 +74,7 @@ function g_game.getSupportedClients()
1040, 1041, 1050, 1051, 1052, 1040, 1041, 1050, 1051, 1052,
1053, 1054, 1055, 1056, 1057, 1053, 1054, 1055, 1056, 1057,
1058, 1059, 1060, 1061, 1062, 1058, 1059, 1060, 1061, 1062,
1063, 1064, 1070, 1071 1063, 1064, 1070, 1071, 1072
} }
end end

View File

@ -2,13 +2,15 @@
ProtocolLogin = extends(Protocol, "ProtocolLogin") ProtocolLogin = extends(Protocol, "ProtocolLogin")
LoginServerError = 10 LoginServerError = 10
LoginServerTokenSuccess = 12
LoginServerTokenError = 13
LoginServerUpdate = 17 LoginServerUpdate = 17
LoginServerMotd = 20 LoginServerMotd = 20
LoginServerUpdateNeeded = 30 LoginServerUpdateNeeded = 30
LoginServerCharacterList = 100 LoginServerCharacterList = 100
LoginServerExtendedCharacterList = 101 LoginServerExtendedCharacterList = 101
function ProtocolLogin:login(host, port, accountName, accountPassword) function ProtocolLogin:login(host, port, accountName, accountPassword, authenticatorToken)
if string.len(host) == 0 or port == nil or port == 0 then if string.len(host) == 0 or port == nil or port == 0 then
signalcall(self.onLoginError, self, tr("You must enter a valid server address and port.")) signalcall(self.onLoginError, self, tr("You must enter a valid server address and port."))
return return
@ -16,6 +18,7 @@ function ProtocolLogin:login(host, port, accountName, accountPassword)
self.accountName = accountName self.accountName = accountName
self.accountPassword = accountPassword self.accountPassword = accountPassword
self.authenticatorToken = authenticatorToken
self.connectCallback = self.sendLoginPacket self.connectCallback = self.sendLoginPacket
self:connect(host, port) self:connect(host, port)
@ -78,7 +81,10 @@ function ProtocolLogin:sendLoginPacket()
local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset) local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
assert(paddingBytes >= 0) assert(paddingBytes >= 0)
msg:addPaddingBytes(paddingBytes, 0) for i = 1, paddingBytes do
msg:addU8(math.random(0, 0xff))
end
if g_game.getFeature(GameLoginPacketEncryption) then if g_game.getFeature(GameLoginPacketEncryption) then
msg:encryptRsa() msg:encryptRsa()
end end
@ -86,10 +92,32 @@ function ProtocolLogin:sendLoginPacket()
if g_game.getFeature(GameOGLInformation) then if g_game.getFeature(GameOGLInformation) then
msg:addU8(1) --unknown msg:addU8(1) --unknown
msg:addU8(1) --unknown msg:addU8(1) --unknown
msg:addString(g_graphics.getRenderer())
if g_game.getClientVersion() >= 1072 then
msg:addString(string.format('%s %s', g_graphics.getVendor(), g_graphics.getRenderer()))
else
msg:addString(g_graphics.getRenderer())
end
msg:addString(g_graphics.getVersion()) msg:addString(g_graphics.getVersion())
end end
-- add RSA encrypted auth token
if g_game.getFeature(GameAuthenticator) then
offset = msg:getMessageSize()
-- first RSA byte must be 0
msg:addU8(0)
msg:addString(self.authenticatorToken)
paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
assert(paddingBytes >= 0)
for i = 1, paddingBytes do
msg:addU8(math.random(0, 0xff))
end
msg:encryptRsa()
end
if g_game.getFeature(GameProtocolChecksum) then if g_game.getFeature(GameProtocolChecksum) then
self:enableChecksum() self:enableChecksum()
end end
@ -116,6 +144,9 @@ function ProtocolLogin:onRecv(msg)
self:parseMotd(msg) self:parseMotd(msg)
elseif opcode == LoginServerUpdateNeeded then elseif opcode == LoginServerUpdateNeeded then
signalcall(self.onLoginError, self, tr("Client needs update.")) signalcall(self.onLoginError, self, tr("Client needs update."))
elseif opcode == LoginServerTokenError then
-- TODO: prompt for token here
signalcall(self.onLoginError, self, tr("Invalid authentification token."))
elseif opcode == LoginServerCharacterList then elseif opcode == LoginServerCharacterList then
self:parseCharacterList(msg) self:parseCharacterList(msg)
elseif opcode == LoginServerExtendedCharacterList then elseif opcode == LoginServerExtendedCharacterList then

View File

@ -399,6 +399,8 @@ namespace Otc
GameClientVersion = 64, GameClientVersion = 64,
GameContentRevision = 65, GameContentRevision = 65,
GameExperienceBonus = 66, GameExperienceBonus = 66,
GameAuthenticator = 67,
GameUnjustifiedPoints = 68,
LastGameFeature = 101 LastGameFeature = 101
}; };

View File

@ -84,6 +84,7 @@ void Game::resetGameStates()
m_localPlayer = nullptr; m_localPlayer = nullptr;
m_pingSent = 0; m_pingSent = 0;
m_pingReceived = 0; m_pingReceived = 0;
m_unjustifiedPoints = UnjustifiedPoints();
for(auto& it : m_containers) { for(auto& it : m_containers) {
const ContainerPtr& container = it.second; const ContainerPtr& container = it.second;
@ -155,6 +156,11 @@ void Game::processLoginWait(const std::string& message, int time)
g_lua.callGlobalField("g_game", "onLoginWait", message, time); g_lua.callGlobalField("g_game", "onLoginWait", message, time);
} }
void Game::processLoginToken(bool unknown)
{
g_lua.callGlobalField("g_game", "onLoginToken", unknown);
}
void Game::processLogin() void Game::processLogin()
{ {
g_lua.callGlobalField("g_game", "onLogin"); g_lua.callGlobalField("g_game", "onLogin");
@ -528,7 +534,7 @@ void Game::processWalkCancel(Otc::Direction direction)
m_localPlayer->cancelWalk(direction); m_localPlayer->cancelWalk(direction);
} }
void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName) void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName, const std::string& authenticatorToken)
{ {
if(m_protocolGame || isOnline()) if(m_protocolGame || isOnline())
stdext::throw_exception("Unable to login into a world while already online or logging."); stdext::throw_exception("Unable to login into a world while already online or logging.");
@ -543,7 +549,7 @@ void Game::loginWorld(const std::string& account, const std::string& password, c
m_localPlayer->setName(characterName); m_localPlayer->setName(characterName);
m_protocolGame = ProtocolGamePtr(new ProtocolGame); m_protocolGame = ProtocolGamePtr(new ProtocolGame);
m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName); m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName, authenticatorToken);
m_characterName = characterName; m_characterName = characterName;
m_worldName = worldName; m_worldName = worldName;
} }
@ -1204,6 +1210,31 @@ void Game::setPVPMode(Otc::PVPModes pvpMode)
g_lua.callGlobalField("g_game", "onPVPModeChange", pvpMode); g_lua.callGlobalField("g_game", "onPVPModeChange", pvpMode);
} }
void Game::setUnjustifiedPoints(UnjustifiedPoints unjustifiedPoints)
{
if(!canPerformGameAction())
return;
if(!getFeature(Otc::GameUnjustifiedPoints))
return;
if(m_unjustifiedPoints == unjustifiedPoints)
return;
m_unjustifiedPoints = unjustifiedPoints;
g_lua.callGlobalField("g_game", "onUnjustifiedPointsChange", unjustifiedPoints);
}
void Game::setOpenPvpSituations(int openPvpSituations)
{
if(!canPerformGameAction())
return;
if(m_openPvpSituations == openPvpSituations)
return;
m_openPvpSituations = openPvpSituations;
g_lua.callGlobalField("g_game", "onOpenPvpSituationsChange", openPvpSituations);
}
void Game::inspectNpcTrade(const ItemPtr& item) void Game::inspectNpcTrade(const ItemPtr& item)
{ {
if(!canPerformGameAction() || !item) if(!canPerformGameAction() || !item)
@ -1425,7 +1456,7 @@ void Game::setProtocolVersion(int version)
if(isOnline()) if(isOnline())
stdext::throw_exception("Unable to change protocol version while online"); stdext::throw_exception("Unable to change protocol version while online");
if(version != 0 && (version < 740 || version > 1071)) if(version != 0 && (version < 740 || version > 1072))
stdext::throw_exception(stdext::format("Protocol version %d not supported", version)); stdext::throw_exception(stdext::format("Protocol version %d not supported", version));
m_protocolVersion = version; m_protocolVersion = version;
@ -1443,7 +1474,7 @@ void Game::setClientVersion(int version)
if(isOnline()) if(isOnline())
stdext::throw_exception("Unable to change client version while online"); stdext::throw_exception("Unable to change client version while online");
if(version != 0 && (version < 740 || version > 1071)) if(version != 0 && (version < 740 || version > 1072))
stdext::throw_exception(stdext::format("Client version %d not supported", version)); stdext::throw_exception(stdext::format("Client version %d not supported", version));
m_features.reset(); m_features.reset();
@ -1563,6 +1594,10 @@ void Game::setClientVersion(int version)
enableFeature(Otc::GameEnhancedAnimations); enableFeature(Otc::GameEnhancedAnimations);
} }
if(version >= 1053) {
enableFeature(Otc::GameUnjustifiedPoints);
}
if(version >= 1054) { if(version >= 1054) {
enableFeature(Otc::GameExperienceBonus); enableFeature(Otc::GameExperienceBonus);
} }
@ -1575,6 +1610,10 @@ void Game::setClientVersion(int version)
enableFeature(Otc::GameContentRevision); enableFeature(Otc::GameContentRevision);
} }
if(version >= 1072) {
enableFeature(Otc::GameAuthenticator);
}
m_clientVersion = version; m_clientVersion = version;
g_lua.callGlobalField("g_game", "onClientVersionChange", version); g_lua.callGlobalField("g_game", "onClientVersionChange", version);

View File

@ -36,6 +36,25 @@
#include <bitset> #include <bitset>
struct UnjustifiedPoints {
bool operator==(const UnjustifiedPoints& other) {
return killsDay == other.killsDay &&
killsDayRemaining == other.killsDayRemaining &&
killsWeek == other.killsWeek &&
killsWeekRemaining == other.killsWeekRemaining &&
killsMonth == other.killsMonth &&
killsMonthRemaining == other.killsMonthRemaining &&
skullTime == other.skullTime;
}
uint8 killsDay;
uint8 killsDayRemaining;
uint8 killsWeek;
uint8 killsWeekRemaining;
uint8 killsMonth;
uint8 killsMonthRemaining;
uint8 skullTime;
};
typedef std::tuple<std::string, uint, std::string, int, bool> Vip; typedef std::tuple<std::string, uint, std::string, int, bool> Vip;
//@bindsingleton g_game //@bindsingleton g_game
@ -60,6 +79,7 @@ protected:
void processLoginError(const std::string& error); void processLoginError(const std::string& error);
void processLoginAdvice(const std::string& message); void processLoginAdvice(const std::string& message);
void processLoginWait(const std::string& message, int time); void processLoginWait(const std::string& message, int time);
void processLoginToken(bool unknown);
void processLogin(); void processLogin();
void processPendingGame(); void processPendingGame();
void processEnterGame(); void processEnterGame();
@ -139,7 +159,7 @@ protected:
public: public:
// login related // login related
void loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName); void loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName, const std::string& authenticatorToken);
void cancelLogin(); void cancelLogin();
void forceLogout(); void forceLogout();
void safeLogout(); void safeLogout();
@ -218,6 +238,12 @@ public:
bool isSafeFight() { return m_safeFight; } bool isSafeFight() { return m_safeFight; }
Otc::PVPModes getPVPMode() { return m_pvpMode; } Otc::PVPModes getPVPMode() { return m_pvpMode; }
// pvp related
void setUnjustifiedPoints(UnjustifiedPoints unjustifiedPoints);
UnjustifiedPoints getUnjustifiedPoints() { return m_unjustifiedPoints; };
void setOpenPvpSituations(int openPvpSitations);
int getOpenPvpSituations() { return m_openPvpSituations; }
// npc trade related // npc trade related
void inspectNpcTrade(const ItemPtr& item); void inspectNpcTrade(const ItemPtr& item);
void buyItem(const ItemPtr& item, int amount, bool ignoreCapacity, bool buyWithBackpack); void buyItem(const ItemPtr& item, int amount, bool ignoreCapacity, bool buyWithBackpack);
@ -304,6 +330,8 @@ public:
int getServerBeat() { return m_serverBeat; } int getServerBeat() { return m_serverBeat; }
void setCanReportBugs(bool enable) { m_canReportBugs = enable; } void setCanReportBugs(bool enable) { m_canReportBugs = enable; }
bool canReportBugs() { return m_canReportBugs; } bool canReportBugs() { return m_canReportBugs; }
void setExpertPvpMode(bool enable) { m_expertPvpMode = enable; }
bool getExpertPvpMode() { return m_expertPvpMode; }
LocalPlayerPtr getLocalPlayer() { return m_localPlayer; } LocalPlayerPtr getLocalPlayer() { return m_localPlayer; }
ProtocolGamePtr getProtocolGame() { return m_protocolGame; } ProtocolGamePtr getProtocolGame() { return m_protocolGame; }
std::string getCharacterName() { return m_characterName; } std::string getCharacterName() { return m_characterName; }
@ -333,6 +361,7 @@ private:
bool m_online; bool m_online;
bool m_denyBotCall; bool m_denyBotCall;
bool m_dead; bool m_dead;
bool m_expertPvpMode;
int m_serverBeat; int m_serverBeat;
ticks_t m_ping; ticks_t m_ping;
uint m_pingSent; uint m_pingSent;
@ -345,6 +374,8 @@ private:
Otc::ChaseModes m_chaseMode; Otc::ChaseModes m_chaseMode;
Otc::PVPModes m_pvpMode; Otc::PVPModes m_pvpMode;
Otc::Direction m_lastWalkDir; Otc::Direction m_lastWalkDir;
UnjustifiedPoints m_unjustifiedPoints;
int m_openPvpSituations;
bool m_safeFight; bool m_safeFight;
bool m_canReportBugs; bool m_canReportBugs;
std::vector<uint8> m_gmActions; std::vector<uint8> m_gmActions;

View File

@ -238,6 +238,10 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "getChaseMode", &Game::getChaseMode, &g_game); g_lua.bindSingletonFunction("g_game", "getChaseMode", &Game::getChaseMode, &g_game);
g_lua.bindSingletonFunction("g_game", "getFightMode", &Game::getFightMode, &g_game); g_lua.bindSingletonFunction("g_game", "getFightMode", &Game::getFightMode, &g_game);
g_lua.bindSingletonFunction("g_game", "getPVPMode", &Game::getPVPMode, &g_game); g_lua.bindSingletonFunction("g_game", "getPVPMode", &Game::getPVPMode, &g_game);
g_lua.bindSingletonFunction("g_game", "setUnjustifiedPoints", &Game::setUnjustifiedPoints, &g_game);
g_lua.bindSingletonFunction("g_game", "getUnjustifiedPoints", &Game::getUnjustifiedPoints, &g_game);
g_lua.bindSingletonFunction("g_game", "setOpenPvpSituations", &Game::setOpenPvpSituations, &g_game);
g_lua.bindSingletonFunction("g_game", "getOpenPvpSituations", &Game::getOpenPvpSituations, &g_game);
g_lua.bindSingletonFunction("g_game", "isSafeFight", &Game::isSafeFight, &g_game); g_lua.bindSingletonFunction("g_game", "isSafeFight", &Game::isSafeFight, &g_game);
g_lua.bindSingletonFunction("g_game", "inspectNpcTrade", &Game::inspectNpcTrade, &g_game); g_lua.bindSingletonFunction("g_game", "inspectNpcTrade", &Game::inspectNpcTrade, &g_game);
g_lua.bindSingletonFunction("g_game", "buyItem", &Game::buyItem, &g_game); g_lua.bindSingletonFunction("g_game", "buyItem", &Game::buyItem, &g_game);

View File

@ -165,3 +165,45 @@ bool luavalue_cast(int index, Light& light)
} }
return false; return false;
} }
int push_luavalue(const UnjustifiedPoints& unjustifiedPoints)
{
g_lua.createTable(0, 7);
g_lua.pushInteger(unjustifiedPoints.killsDay);
g_lua.setField("killsDay");
g_lua.pushInteger(unjustifiedPoints.killsDayRemaining);
g_lua.setField("killsDayRemaining");
g_lua.pushInteger(unjustifiedPoints.killsWeek);
g_lua.setField("killsWeek");
g_lua.pushInteger(unjustifiedPoints.killsWeekRemaining);
g_lua.setField("killsWeekRemaining");
g_lua.pushInteger(unjustifiedPoints.killsMonth);
g_lua.setField("killsMonth");
g_lua.pushInteger(unjustifiedPoints.killsMonthRemaining);
g_lua.setField("killsMonthRemaining");
g_lua.pushInteger(unjustifiedPoints.skullTime);
g_lua.setField("skullTime");
return 1;
}
bool luavalue_cast(int index, UnjustifiedPoints& unjustifiedPoints)
{
if(g_lua.isTable(index)) {
g_lua.getField("killsDay", index);
unjustifiedPoints.killsDay = g_lua.popInteger();
g_lua.getField("killsDayRemaining", index);
unjustifiedPoints.killsDayRemaining = g_lua.popInteger();
g_lua.getField("killsWeek", index);
unjustifiedPoints.killsWeek = g_lua.popInteger();
g_lua.getField("killsWeekRemaining", index);
unjustifiedPoints.killsWeekRemaining = g_lua.popInteger();
g_lua.getField("killsMonth", index);
unjustifiedPoints.killsMonth = g_lua.popInteger();
g_lua.getField("killsMonthRemaining", index);
unjustifiedPoints.killsMonthRemaining = g_lua.popInteger();
g_lua.getField("skullTime", index);
unjustifiedPoints.skullTime = g_lua.popInteger();
return true;
}
return false;
}

View File

@ -44,4 +44,8 @@ bool luavalue_cast(int index, MarketData& data);
int push_luavalue(const Light& light); int push_luavalue(const Light& light);
bool luavalue_cast(int index, Light& light); bool luavalue_cast(int index, Light& light);
// unjustified points
int push_luavalue(const UnjustifiedPoints& unjustifiedPoints);
bool luavalue_cast(int index, UnjustifiedPoints& unjustifiedPoints);
#endif #endif

View File

@ -50,6 +50,7 @@ namespace Proto {
GameServerLoginAdvice = 21, GameServerLoginAdvice = 21,
GameServerLoginWait = 22, GameServerLoginWait = 22,
GameServerLoginSuccess = 23, GameServerLoginSuccess = 23,
GameServerLoginToken = 24,
GameServerPingBack = 29, GameServerPingBack = 29,
GameServerPing = 30, GameServerPing = 30,
GameServerChallenge = 31, GameServerChallenge = 31,

View File

@ -26,10 +26,11 @@
#include "item.h" #include "item.h"
#include "localplayer.h" #include "localplayer.h"
void ProtocolGame::login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName) void ProtocolGame::login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName, const std::string& authenticatorToken)
{ {
m_accountName = accountName; m_accountName = accountName;
m_accountPassword = accountPassword; m_accountPassword = accountPassword;
m_authenticatorToken = authenticatorToken;
m_characterName = characterName; m_characterName = characterName;
connect(host, port); connect(host, port);

View File

@ -31,7 +31,7 @@
class ProtocolGame : public Protocol class ProtocolGame : public Protocol
{ {
public: public:
void login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName); void login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName, const std::string& authenticatorToken);
void send(const OutputMessagePtr& outputMessage); void send(const OutputMessagePtr& outputMessage);
void sendExtendedOpcode(uint8 opcode, const std::string& buffer); void sendExtendedOpcode(uint8 opcode, const std::string& buffer);
@ -143,6 +143,7 @@ private:
void parseLoginError(const InputMessagePtr& msg); void parseLoginError(const InputMessagePtr& msg);
void parseLoginAdvice(const InputMessagePtr& msg); void parseLoginAdvice(const InputMessagePtr& msg);
void parseLoginWait(const InputMessagePtr& msg); void parseLoginWait(const InputMessagePtr& msg);
void parseLoginToken(const InputMessagePtr& msg);
void parsePing(const InputMessagePtr& msg); void parsePing(const InputMessagePtr& msg);
void parsePingBack(const InputMessagePtr& msg); void parsePingBack(const InputMessagePtr& msg);
void parseChallenge(const InputMessagePtr& msg); void parseChallenge(const InputMessagePtr& msg);
@ -246,6 +247,7 @@ private:
stdext::boolean<true> m_firstRecv; stdext::boolean<true> m_firstRecv;
std::string m_accountName; std::string m_accountName;
std::string m_accountPassword; std::string m_accountPassword;
std::string m_authenticatorToken;
std::string m_characterName; std::string m_characterName;
LocalPlayerPtr m_localPlayer; LocalPlayerPtr m_localPlayer;
}; };

View File

@ -79,6 +79,9 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
case Proto::GameServerLoginWait: case Proto::GameServerLoginWait:
parseLoginWait(msg); parseLoginWait(msg);
break; break;
case Proto::GameServerLoginToken:
parseLoginToken(msg);
break;
case Proto::GameServerPing: case Proto::GameServerPing:
case Proto::GameServerPingBack: case Proto::GameServerPingBack:
if((opcode == Proto::GameServerPing && g_game.getFeature(Otc::GameClientPing)) || if((opcode == Proto::GameServerPing && g_game.getFeature(Otc::GameClientPing)) ||
@ -385,11 +388,13 @@ void ProtocolGame::parseLogin(const InputMessagePtr& msg)
} }
bool canReportBugs = msg->getU8(); bool canReportBugs = msg->getU8();
if(g_game.getClientVersion() >= 1053) if(g_game.getClientVersion() >= 1054)
msg->getU8(); // can change pvp frame option msg->getU8(); // can change pvp frame option
if(g_game.getClientVersion() >= 1058) if(g_game.getClientVersion() >= 1058) {
msg->getU8(); // expert mode enabled int expertModeEnabled = msg->getU8();
g_game.setExpertPvpMode(expertModeEnabled);
}
m_localPlayer->setId(playerId); m_localPlayer->setId(playerId);
g_game.setServerBeat(serverBeat); g_game.setServerBeat(serverBeat);
@ -428,19 +433,23 @@ void ProtocolGame::parsePreset(const InputMessagePtr& msg)
void ProtocolGame::parseUnjustifiedStats(const InputMessagePtr& msg) void ProtocolGame::parseUnjustifiedStats(const InputMessagePtr& msg)
{ {
// Unjustified Kills display since 10.55 UnjustifiedPoints unjustifiedPoints;
msg->getU8(); unjustifiedPoints.killsDay = msg->getU8();
msg->getU8(); unjustifiedPoints.killsDayRemaining = msg->getU8();
msg->getU8(); unjustifiedPoints.killsWeek = msg->getU8();
msg->getU8(); unjustifiedPoints.killsWeekRemaining = msg->getU8();
msg->getU8(); unjustifiedPoints.killsMonth = msg->getU8();
msg->getU8(); unjustifiedPoints.killsMonthRemaining = msg->getU8();
msg->getU8(); unjustifiedPoints.skullTime = msg->getU8();
g_game.setUnjustifiedPoints(unjustifiedPoints);
} }
void ProtocolGame::parsePvpSituations(const InputMessagePtr& msg) void ProtocolGame::parsePvpSituations(const InputMessagePtr& msg)
{ {
msg->getU8(); // amount of open pvp situations uint8 openPvpSituations = msg->getU8();
g_game.setOpenPvpSituations(openPvpSituations);
} }
void ProtocolGame::parsePlayerHelpers(const InputMessagePtr& msg) void ProtocolGame::parsePlayerHelpers(const InputMessagePtr& msg)
@ -501,6 +510,12 @@ void ProtocolGame::parseLoginWait(const InputMessagePtr& msg)
g_game.processLoginWait(message, time); g_game.processLoginWait(message, time);
} }
void ProtocolGame::parseLoginToken(const InputMessagePtr& msg)
{
bool unknown = (msg->getU8() == 0);
g_game.processLoginToken(unknown);
}
void ProtocolGame::parsePing(const InputMessagePtr& msg) void ProtocolGame::parsePing(const InputMessagePtr& msg)
{ {
g_game.processPing(); g_game.processPing();

View File

@ -87,6 +87,9 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addString(m_characterName); msg->addString(m_characterName);
msg->addString(m_accountPassword); msg->addString(m_accountPassword);
if(g_game.getFeature(Otc::GameAuthenticator))
msg->addString(m_authenticatorToken);
if(g_game.getFeature(Otc::GameChallengeOnLogin)) { if(g_game.getFeature(Otc::GameChallengeOnLogin)) {
msg->addU32(challengeTimestamp); msg->addU32(challengeTimestamp);
msg->addU8(challengeRandom); msg->addU8(challengeRandom);