Browse Source

Protocol 10.72 (Authenticator) Support, Unjustified Points diplay

- Unjustified Points (Better topbar icon would be nice)
![Unjustified Points](http://i.gyazo.com/81286f46d9.png)
- Authenticator token support
- adjusted 'can change pvp frame' to 1054
- ...
TheSumm 6 years ago
parent
commit
ddec9627b8

BIN
data/images/game/skull_socket.png View File


BIN
data/images/topbuttons/unjustifiedpoints.png View File


+ 13
- 1
modules/client_entergame/characterlist.lua View File

@@ -27,7 +27,7 @@ local function tryLogin(charInfo, tries)
27 27
 
28 28
   CharacterList.hide()
29 29
 
30
-  g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName)
30
+  g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName, G.authenticatorToken)
31 31
 
32 32
   loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to game server...'))
33 33
   connect(loadBox, { onCancel = function()
@@ -109,6 +109,16 @@ function onGameLoginError(message)
109 109
   end
110 110
 end
111 111
 
112
+function onGameLoginToken(unknown)
113
+  CharacterList.destroyLoadBox()
114
+  -- TODO: make it possible to enter a new token here / prompt token
115
+  errorBox = displayErrorBox(tr("Two-Factor Authentification"), 'A new authentification token is required.\nPlease login again.')
116
+  errorBox.onOk = function()
117
+    errorBox = nil
118
+    EnterGame.show()
119
+  end
120
+end
121
+
112 122
 function onGameConnectionError(message, code)
113 123
   CharacterList.destroyLoadBox()
114 124
   local text = translateNetworkError(code, g_game.getProtocolGame() and g_game.getProtocolGame():isConnecting(), message)
@@ -131,6 +141,7 @@ end
131 141
 -- public functions
132 142
 function CharacterList.init()
133 143
   connect(g_game, { onLoginError = onGameLoginError })
144
+  connect(g_game, { onLoginToken = onGameLoginToken })
134 145
   connect(g_game, { onUpdateNeeded = onGameUpdateNeeded })
135 146
   connect(g_game, { onConnectionError = onGameConnectionError })
136 147
   connect(g_game, { onGameStart = CharacterList.destroyLoadBox })
@@ -144,6 +155,7 @@ end
144 155
 
145 156
 function CharacterList.terminate()
146 157
   disconnect(g_game, { onLoginError = onGameLoginError })
158
+  disconnect(g_game, { onLoginToken = onGameLoginToken })
147 159
   disconnect(g_game, { onUpdateNeeded = onGameUpdateNeeded })
148 160
   disconnect(g_game, { onConnectionError = onGameConnectionError })
149 161
   disconnect(g_game, { onGameStart = CharacterList.destroyLoadBox })

+ 3
- 3
modules/client_entergame/characterlist.otui View File

@@ -16,7 +16,7 @@ CharacterWidget < UIWidget
16 16
 
17 17
   Label
18 18
     id: name
19
-    color: #aaaaaa
19
+    color: #bbbbbb
20 20
     anchors.top: parent.top
21 21
     anchors.left: parent.left
22 22
     font: verdana-11px-monochrome
@@ -29,8 +29,7 @@ CharacterWidget < UIWidget
29 29
 
30 30
   Label
31 31
     id: worldName
32
-    color: #ffffff
33
-    color: #aaaaaa
32
+    color: #bbbbbb
34 33
     anchors.top: parent.top
35 34
     anchors.right: parent.right
36 35
     margin-right: 5
@@ -59,6 +58,7 @@ MainWindow
59 58
 
60 59
   TextList
61 60
     id: characters
61
+    background-color: #565656
62 62
     anchors.top: parent.top
63 63
     anchors.left: parent.left
64 64
     anchors.right: characterListScrollBar.left

+ 57
- 3
modules/client_entergame/entergame.lua View File

@@ -111,7 +111,7 @@ function EnterGame.init()
111 111
   local port = g_settings.get('port')
112 112
   local autologin = g_settings.getBoolean('autologin')
113 113
   local clientVersion = g_settings.getInteger('client-version')
114
-  if clientVersion == 0 then clientVersion = 1071 end
114
+  if clientVersion == 0 then clientVersion = 1072 end
115 115
 
116 116
   if port == nil or port == 0 then port = 7171 end
117 117
 
@@ -122,7 +122,10 @@ function EnterGame.init()
122 122
   enterGame:getChildById('serverPortTextEdit'):setText(port)
123 123
   enterGame:getChildById('autoLoginBox'):setChecked(autologin)
124 124
 
125
+
125 126
   clientBox = enterGame:getChildById('clientComboBox')
127
+  connect(clientBox, { onOptionChange = EnterGame.onClientVersionChange })
128
+
126 129
   for _, proto in pairs(g_game.getSupportedClients()) do
127 130
     clientBox:addOption(proto)
128 131
   end
@@ -152,6 +155,7 @@ end
152 155
 
153 156
 function EnterGame.terminate()
154 157
   g_keyboard.unbindKeyDown('Ctrl+G')
158
+  disconnect(clientBox, { onOptionChange = EnterGame.onClientVersionChange })
155 159
   enterGame:destroy()
156 160
   enterGame = nil
157 161
   enterGameButton:destroy()
@@ -210,14 +214,55 @@ end
210 214
 function EnterGame.clearAccountFields()
211 215
   enterGame:getChildById('accountNameTextEdit'):clearText()
212 216
   enterGame:getChildById('accountPasswordTextEdit'):clearText()
217
+  enterGame:getChildById('authenticatorTokenTextEdit'):clearText()
213 218
   enterGame:getChildById('accountNameTextEdit'):focus()
214 219
   g_settings.remove('account')
215 220
   g_settings.remove('password')
216 221
 end
217 222
 
223
+function EnterGame.toggleAuthenticatorToken(enabled)
224
+  if enabled == enterGame.authenticatorEnabled then
225
+    return
226
+  end
227
+
228
+  if enabled then
229
+    enterGame:getChildById('authenticatorTokenLabel'):setVisible(true)
230
+    enterGame:getChildById('authenticatorTokenTextEdit'):setVisible(true)
231
+
232
+    local serverLabel = enterGame:getChildById('serverLabel')
233
+    serverLabel:setMarginTop(serverLabel:getMarginTop() + enterGame.authenticatorHeight)
234
+
235
+    enterGame:breakAnchors()
236
+    enterGame:setY(enterGame:getY() - enterGame.authenticatorHeight)
237
+    enterGame:bindRectToParent()
238
+
239
+    enterGame:setHeight(enterGame:getHeight() + enterGame.authenticatorHeight)
240
+  else
241
+    enterGame:getChildById('authenticatorTokenLabel'):setVisible(false)
242
+    enterGame:getChildById('authenticatorTokenTextEdit'):setVisible(false)
243
+
244
+    local serverLabel = enterGame:getChildById('serverLabel')
245
+    serverLabel:setMarginTop(serverLabel:getMarginTop() - enterGame.authenticatorHeight)
246
+
247
+    enterGame:breakAnchors()
248
+    enterGame:setY(enterGame:getY() + enterGame.authenticatorHeight)
249
+    enterGame:bindRectToParent()
250
+
251
+    enterGame:setHeight(enterGame:getHeight() - enterGame.authenticatorHeight)
252
+  end
253
+
254
+  enterGame.authenticatorEnabled = enabled
255
+end
256
+
257
+function EnterGame.onClientVersionChange(comboBox, text, data)
258
+  local clientVersion = tonumber(text)
259
+  EnterGame.toggleAuthenticatorToken(clientVersion >= 1072)
260
+end
261
+
218 262
 function EnterGame.doLogin()
219 263
   G.account = enterGame:getChildById('accountNameTextEdit'):getText()
220 264
   G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
265
+  G.authenticatorToken = enterGame:getChildById('authenticatorTokenTextEdit'):getText()
221 266
   G.host = enterGame:getChildById('serverHostTextEdit'):getText()
222 267
   G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText())
223 268
   local clientVersion = tonumber(clientBox:getText())
@@ -251,7 +296,7 @@ function EnterGame.doLogin()
251 296
   g_game.chooseRsa(G.host)
252 297
 
253 298
   if modules.game_things.isLoaded() then
254
-    protocolLogin:login(G.host, G.port, G.account, G.password)
299
+    protocolLogin:login(G.host, G.port, G.account, G.password, G.authenticatorToken)
255 300
   else
256 301
     loadBox:destroy()
257 302
     loadBox = nil
@@ -272,6 +317,7 @@ function EnterGame.setDefaultServer(host, port, protocol)
272 317
   local clientLabel = enterGame:getChildById('clientLabel')
273 318
   local accountTextEdit = enterGame:getChildById('accountNameTextEdit')
274 319
   local passwordTextEdit = enterGame:getChildById('accountPasswordTextEdit')
320
+  local authenticatorTokenTextEdit = enterGame:getChildById('authenticatorTokenTextEdit')
275 321
 
276 322
   if hostTextEdit:getText() ~= host then
277 323
     hostTextEdit:setText(host)
@@ -279,6 +325,7 @@ function EnterGame.setDefaultServer(host, port, protocol)
279 325
     clientBox:setCurrentOption(protocol)
280 326
     accountTextEdit:setText('')
281 327
     passwordTextEdit:setText('')
328
+    authenticatorTokenTextEdit:setText('')
282 329
   end
283 330
 end
284 331
 
@@ -291,6 +338,13 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig
291 338
   portTextEdit:setText(port)
292 339
   portTextEdit:setVisible(false)
293 340
   portTextEdit:setHeight(0)
341
+  local authenticatorTokenTextEdit = enterGame:getChildById('authenticatorTokenTextEdit')
342
+  authenticatorTokenTextEdit:setText('')
343
+  authenticatorTokenTextEdit:setVisible(false)
344
+  authenticatorTokenTextEdit:setHeight(0)
345
+  local authenticatorTokenLabel = enterGame:getChildById('authenticatorTokenLabel')
346
+  authenticatorTokenLabel:setVisible(false)
347
+  authenticatorTokenLabel:setHeight(0)
294 348
 
295 349
   clientBox:setCurrentOption(protocol)
296 350
   clientBox:setVisible(false)
@@ -312,7 +366,7 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig
312 366
   serverListButton:setWidth(0)
313 367
 
314 368
   local rememberPasswordBox = enterGame:getChildById('rememberPasswordBox')
315
-  rememberPasswordBox:setMarginTop(-5)
369
+  rememberPasswordBox:setMarginTop(-14)
316 370
 
317 371
   if not windowWidth then windowWidth = 236 end
318 372
   enterGame:setWidth(windowWidth)

+ 21
- 1
modules/client_entergame/entergame.otui View File

@@ -21,6 +21,8 @@ ServerListButton < UIButton
21 21
 
22 22
 EnterGameWindow
23 23
   id: enterGame
24
+  &authenticatorEnabled: false
25
+  &authenticatorHeight: 44
24 26
   @onEnter: EnterGame.doLogin()
25 27
 
26 28
   MenuLabel
@@ -50,12 +52,30 @@ EnterGameWindow
50 52
     anchors.top: prev.bottom
51 53
     margin-top: 2
52 54
 
55
+  MenuLabel
56
+    id: authenticatorTokenLabel
57
+    !text: tr('Authenticator Token')
58
+    anchors.left: prev.left
59
+    anchors.top: prev.bottom
60
+    text-auto-resize: true
61
+    margin-top: 8
62
+    visible: false
63
+
64
+  TextEdit
65
+    id: authenticatorTokenTextEdit
66
+    anchors.left: parent.left
67
+    anchors.right: parent.right
68
+    anchors.top: prev.bottom
69
+    margin-top: 2
70
+    visible: false
71
+    max-length: 8
72
+
53 73
   MenuLabel
54 74
     id: serverLabel
55 75
     !text: tr('Server')
56 76
     anchors.left: prev.left
57 77
     anchors.top: prev.bottom
58
-    margin-top: 8
78
+    margin-top: -36
59 79
     text-auto-resize: true
60 80
 
61 81
   ServerListButton

+ 1
- 0
modules/game_interface/interface.otmod View File

@@ -30,5 +30,6 @@ Module
30 30
     - game_spelllist
31 31
     - game_cooldown
32 32
     - game_modaldialog
33
+    - game_unjustifiedpoints
33 34
   @onLoad: init()
34 35
   @onUnload: terminate()

+ 135
- 0
modules/game_unjustifiedpoints/unjustifiedpoints.lua View File

@@ -0,0 +1,135 @@
1
+unjustifiedPointsWindow = nil
2
+unjustifiedPointsButton = nil
3
+contentsPanel = nil
4
+
5
+openPvpSituationsLabel = nil
6
+currentSkullWidget = nil
7
+skullTimeLabel = nil
8
+
9
+dayProgressBar = nil
10
+weekProgressBar = nil
11
+monthProgressBar = nil
12
+
13
+daySkullWidget = nil
14
+weekSkullWidget = nil
15
+monthSkullWidget = nil
16
+
17
+function init()
18
+  connect(g_game, { onGameStart = online,
19
+                    onUnjustifiedPointsChange = onUnjustifiedPointsChange,
20
+                    onOpenPvpSituationsChange = onOpenPvpSituationsChange })
21
+  connect(LocalPlayer, { onSkullChange = onSkullChange } )
22
+
23
+  unjustifiedPointsButton = modules.client_topmenu.addRightGameToggleButton('unjustifiedPointsButton',
24
+    tr('Unjustified Points'), '/images/topbuttons/unjustifiedpoints', toggle)
25
+  unjustifiedPointsButton:setOn(true)
26
+  unjustifiedPointsButton:hide()
27
+
28
+  unjustifiedPointsWindow = g_ui.loadUI('unjustifiedpoints', modules.game_interface.getRightPanel())
29
+  unjustifiedPointsWindow:disableResize()
30
+  unjustifiedPointsWindow:setup()
31
+
32
+  contentsPanel = unjustifiedPointsWindow:getChildById('contentsPanel')
33
+
34
+  openPvpSituationsLabel = contentsPanel:getChildById('openPvpSituationsLabel')
35
+  currentSkullWidget = contentsPanel:getChildById('currentSkullWidget')
36
+  skullTimeLabel = contentsPanel:getChildById('skullTimeLabel')
37
+
38
+  dayProgressBar = contentsPanel:getChildById('dayProgressBar')
39
+  weekProgressBar = contentsPanel:getChildById('weekProgressBar')
40
+  monthProgressBar = contentsPanel:getChildById('monthProgressBar')
41
+  daySkullWidget = contentsPanel:getChildById('daySkullWidget')
42
+  weekSkullWidget = contentsPanel:getChildById('weekSkullWidget')
43
+  monthSkullWidget = contentsPanel:getChildById('monthSkullWidget')
44
+
45
+  if g_game.isOnline() then
46
+    online()
47
+  end
48
+end
49
+
50
+function terminate()
51
+  disconnect(g_game, { onGameStart = online,
52
+                       onUnjustifiedPointsChange = onUnjustifiedPointsChange,
53
+                       onOpenPvpSituationsChange = onOpenPvpSituationsChange })
54
+  disconnect(LocalPlayer, { onSkullChange = onSkullChange } )
55
+
56
+  unjustifiedPointsWindow:destroy()
57
+  unjustifiedPointsButton:destroy()
58
+end
59
+
60
+function onMiniWindowClose()
61
+  unjustifiedPointsButton:setOn(false)
62
+end
63
+
64
+function toggle()
65
+  if unjustifiedPointsButton:isOn() then
66
+    unjustifiedPointsWindow:close()
67
+    unjustifiedPointsButton:setOn(false)
68
+  else
69
+    unjustifiedPointsWindow:open()
70
+    unjustifiedPointsButton:setOn(true)
71
+  end
72
+end
73
+
74
+function online()
75
+  if g_game.getFeature(GameUnjustifiedPoints) then
76
+    unjustifiedPointsButton:show()
77
+  else
78
+    unjustifiedPointsButton:hide()
79
+    unjustifiedPointsWindow:close()
80
+  end
81
+
82
+  refresh()
83
+end
84
+
85
+function refresh()
86
+  local localPlayer = g_game.getLocalPlayer()
87
+
88
+  local unjustifiedPoints = g_game.getUnjustifiedPoints()
89
+  onUnjustifiedPointsChange(unjustifiedPoints)
90
+
91
+  onSkullChange(localPlayer, localPlayer:getSkull())
92
+  onOpenPvpSituationsChange(g_game.getOpenPvpSituations())
93
+end
94
+
95
+function onSkullChange(localPlayer, skull)
96
+  if not localPlayer:isLocalPlayer() then return end
97
+
98
+  if skull == SkullRed or skull == SkullBlack then
99
+    currentSkullWidget:setIcon(getSkullImagePath(skull))
100
+    currentSkullWidget:setTooltip('Remaining skull time')
101
+  else
102
+    currentSkullWidget:setIcon('')
103
+    currentSkullWidget:setTooltip('You have no skull')
104
+  end
105
+
106
+  daySkullWidget:setIcon(getSkullImagePath(getNextSkullId(skull)))
107
+  weekSkullWidget:setIcon(getSkullImagePath(getNextSkullId(skull)))
108
+  monthSkullWidget:setIcon(getSkullImagePath(getNextSkullId(skull)))
109
+end
110
+
111
+function onOpenPvpSituationsChange(amount)
112
+  openPvpSituationsLabel:setText(amount)
113
+end
114
+
115
+function onUnjustifiedPointsChange(unjustifiedPoints)
116
+  if unjustifiedPoints.skullTime == 0 then
117
+    skullTimeLabel:setText('No skull')
118
+    skullTimeLabel:setTooltip('You have no skull')
119
+  else
120
+    skullTimeLabel:setText(unjustifiedPoints.skullTime .. ' days')
121
+    skullTimeLabel:setTooltip('Remaining skull time')
122
+  end
123
+
124
+  dayProgressBar:setValue(unjustifiedPoints.killsDay, 0, 100)
125
+  dayProgressBar:setTooltip(string.format('Unjustified points gained during the last 24 hours.\n%i kills left.', unjustifiedPoints.killsDayRemaining))
126
+  dayProgressBar:setText(unjustifiedPoints.killsDayRemaining .. ' kills left')
127
+
128
+  weekProgressBar:setValue(unjustifiedPoints.killsWeek, 0, 100)
129
+  weekProgressBar:setTooltip(string.format('Unjustified points gained during the last 7 days.\n%i kills left.', unjustifiedPoints.killsWeekRemaining))
130
+  weekProgressBar:setText(unjustifiedPoints.killsWeekRemaining .. ' kills left')
131
+
132
+  monthProgressBar:setValue(unjustifiedPoints.killsMonth, 0, 100)
133
+  monthProgressBar:setTooltip(string.format('Unjustified points gained during the last 30 days.\n%i kills left.', unjustifiedPoints.killsMonthRemaining))
134
+  monthProgressBar:setText(unjustifiedPoints.killsMonthRemaining .. ' kills left')
135
+end

+ 8
- 0
modules/game_unjustifiedpoints/unjustifiedpoints.otmod View File

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

+ 80
- 0
modules/game_unjustifiedpoints/unjustifiedpoints.otui View File

@@ -0,0 +1,80 @@
1
+SkullProgressBar < ProgressBar
2
+  height: 13
3
+  margin: 4 18 0 10
4
+  anchors.top: prev.bottom
5
+  anchors.left: parent.left
6
+  anchors.right: parent.right
7
+
8
+SkullWidget < UIWidget
9
+  size: 13 13
10
+  margin-right: 2
11
+  anchors.right: parent.right
12
+  image-source: /images/game/skull_socket
13
+
14
+MiniWindow
15
+  id: unjustifiedPointsWindow
16
+  !text: tr('Unjustified Points')
17
+  height: 114
18
+  icon: /images/topbuttons/unjustifiedpoints
19
+  @onClose: modules.game_unjustifiedpoints.onMiniWindowClose()
20
+  &save: true
21
+
22
+  MiniWindowContents
23
+    Label
24
+      anchors.top: parent.top
25
+      anchors.left: parent.left
26
+      !text: tr('Open PvP')
27
+      !tooltip: tr('Open PvP Situations')
28
+      phantom: false
29
+      margin-top: 2
30
+      margin-left: 10
31
+
32
+    Label
33
+      id: openPvpSituationsLabel
34
+      anchors.top: prev.bottom
35
+      anchors.left: parent.left
36
+      font: verdana-11px-rounded
37
+      margin-left: 12
38
+      phantom: false
39
+
40
+    Label
41
+      anchors.top: parent.top
42
+      anchors.right: parent.right
43
+      !text: tr('Skull Time')
44
+      margin-top: 2
45
+      margin-right: 10
46
+
47
+    SkullWidget
48
+      id: currentSkullWidget
49
+      anchors.top: prev.bottom
50
+      margin-right: 10
51
+
52
+    Label
53
+      id: skullTimeLabel
54
+      anchors.top: prev.top
55
+      anchors.right: prev.left
56
+      font: verdana-11px-rounded
57
+      margin-right: 6
58
+      phantom: false
59
+
60
+    SkullProgressBar
61
+      id: dayProgressBar
62
+      margin-top: 10
63
+
64
+    SkullWidget
65
+      id: daySkullWidget
66
+      anchors.top: prev.top
67
+
68
+    SkullProgressBar
69
+      id: weekProgressBar
70
+
71
+    SkullWidget
72
+      id: weekSkullWidget
73
+      anchors.top: prev.top
74
+
75
+    SkullProgressBar
76
+      id: monthProgressBar
77
+
78
+    SkullWidget
79
+      id: monthSkullWidget
80
+      anchors.top: prev.top

+ 2
- 0
modules/gamelib/const.lua View File

@@ -127,6 +127,8 @@ GameLoginPacketEncryption = 63
127 127
 GameClientVersion = 64
128 128
 GameContentRevision = 65
129 129
 GameExperienceBonus = 66
130
+GameAuthenticator = 67
131
+GameUnjustifiedPoints = 68
130 132
 
131 133
 TextColors = {
132 134
   red       = '#f55e5e', --'#c83200'

+ 7
- 0
modules/gamelib/creature.lua View File

@@ -35,6 +35,13 @@ NpcIconTradeQuest = 4
35 35
 
36 36
 -- @}
37 37
 
38
+function getNextSkullId(skullId)
39
+  if skullId == SkullRed or skullId == SkullBlack then
40
+    return SkullBlack
41
+  end
42
+  return SkullRed
43
+end
44
+
38 45
 function getSkullImagePath(skullId)
39 46
   local path
40 47
   if skullId == SkullYellow then

+ 1
- 1
modules/gamelib/game.lua View File

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

+ 34
- 3
modules/gamelib/protocollogin.lua View File

@@ -2,13 +2,15 @@
2 2
 ProtocolLogin = extends(Protocol, "ProtocolLogin")
3 3
 
4 4
 LoginServerError = 10
5
+LoginServerTokenSuccess = 12
6
+LoginServerTokenError = 13
5 7
 LoginServerUpdate = 17
6 8
 LoginServerMotd = 20
7 9
 LoginServerUpdateNeeded = 30
8 10
 LoginServerCharacterList = 100
9 11
 LoginServerExtendedCharacterList = 101
10 12
 
11
-function ProtocolLogin:login(host, port, accountName, accountPassword)
13
+function ProtocolLogin:login(host, port, accountName, accountPassword, authenticatorToken)
12 14
   if string.len(host) == 0 or port == nil or port == 0 then
13 15
     signalcall(self.onLoginError, self, tr("You must enter a valid server address and port."))
14 16
     return
@@ -16,6 +18,7 @@ function ProtocolLogin:login(host, port, accountName, accountPassword)
16 18
 
17 19
   self.accountName = accountName
18 20
   self.accountPassword = accountPassword
21
+  self.authenticatorToken = authenticatorToken
19 22
   self.connectCallback = self.sendLoginPacket
20 23
 
21 24
   self:connect(host, port)
@@ -78,7 +81,10 @@ function ProtocolLogin:sendLoginPacket()
78 81
 
79 82
   local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
80 83
   assert(paddingBytes >= 0)
81
-  msg:addPaddingBytes(paddingBytes, 0)
84
+  for i = 1, paddingBytes do
85
+    msg:addU8(math.random(0, 0xff))
86
+  end
87
+
82 88
   if g_game.getFeature(GameLoginPacketEncryption) then
83 89
     msg:encryptRsa()
84 90
   end
@@ -86,10 +92,32 @@ function ProtocolLogin:sendLoginPacket()
86 92
   if g_game.getFeature(GameOGLInformation) then
87 93
     msg:addU8(1) --unknown
88 94
     msg:addU8(1) --unknown
89
-    msg:addString(g_graphics.getRenderer())
95
+
96
+    if g_game.getClientVersion() >= 1072 then
97
+      msg:addString(string.format('%s %s', g_graphics.getVendor(), g_graphics.getRenderer()))
98
+    else
99
+      msg:addString(g_graphics.getRenderer())
100
+    end
90 101
     msg:addString(g_graphics.getVersion())
91 102
   end
92 103
 
104
+  -- add RSA encrypted auth token
105
+  if g_game.getFeature(GameAuthenticator) then
106
+    offset = msg:getMessageSize()
107
+
108
+    -- first RSA byte must be 0
109
+    msg:addU8(0)
110
+    msg:addString(self.authenticatorToken)
111
+
112
+    paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
113
+    assert(paddingBytes >= 0)
114
+    for i = 1, paddingBytes do
115
+      msg:addU8(math.random(0, 0xff))
116
+    end
117
+
118
+    msg:encryptRsa()
119
+  end
120
+
93 121
   if g_game.getFeature(GameProtocolChecksum) then
94 122
     self:enableChecksum()
95 123
   end
@@ -116,6 +144,9 @@ function ProtocolLogin:onRecv(msg)
116 144
       self:parseMotd(msg)
117 145
     elseif opcode == LoginServerUpdateNeeded then
118 146
       signalcall(self.onLoginError, self, tr("Client needs update."))
147
+    elseif opcode == LoginServerTokenError then
148
+      -- TODO: prompt for token here
149
+      signalcall(self.onLoginError, self, tr("Invalid authentification token."))
119 150
     elseif opcode == LoginServerCharacterList then
120 151
       self:parseCharacterList(msg)
121 152
     elseif opcode == LoginServerExtendedCharacterList then

+ 2
- 0
src/client/const.h View File

@@ -399,6 +399,8 @@ namespace Otc
399 399
         GameClientVersion = 64,
400 400
         GameContentRevision = 65,
401 401
         GameExperienceBonus = 66,
402
+        GameAuthenticator = 67,
403
+        GameUnjustifiedPoints = 68,
402 404
 
403 405
         LastGameFeature = 101
404 406
     };

+ 43
- 4
src/client/game.cpp View File

@@ -84,6 +84,7 @@ void Game::resetGameStates()
84 84
     m_localPlayer = nullptr;
85 85
     m_pingSent = 0;
86 86
     m_pingReceived = 0;
87
+    m_unjustifiedPoints = UnjustifiedPoints();
87 88
 
88 89
     for(auto& it : m_containers) {
89 90
         const ContainerPtr& container = it.second;
@@ -155,6 +156,11 @@ void Game::processLoginWait(const std::string& message, int time)
155 156
     g_lua.callGlobalField("g_game", "onLoginWait", message, time);
156 157
 }
157 158
 
159
+void Game::processLoginToken(bool unknown)
160
+{
161
+    g_lua.callGlobalField("g_game", "onLoginToken", unknown);
162
+}
163
+
158 164
 void Game::processLogin()
159 165
 {
160 166
     g_lua.callGlobalField("g_game", "onLogin");
@@ -528,7 +534,7 @@ void Game::processWalkCancel(Otc::Direction direction)
528 534
     m_localPlayer->cancelWalk(direction);
529 535
 }
530 536
 
531
-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)
537
+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)
532 538
 {
533 539
     if(m_protocolGame || isOnline())
534 540
         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
543 549
     m_localPlayer->setName(characterName);
544 550
 
545 551
     m_protocolGame = ProtocolGamePtr(new ProtocolGame);
546
-    m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName);
552
+    m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName, authenticatorToken);
547 553
     m_characterName = characterName;
548 554
     m_worldName = worldName;
549 555
 }
@@ -1204,6 +1210,31 @@ void Game::setPVPMode(Otc::PVPModes pvpMode)
1204 1210
     g_lua.callGlobalField("g_game", "onPVPModeChange", pvpMode);
1205 1211
 }
1206 1212
 
1213
+void Game::setUnjustifiedPoints(UnjustifiedPoints unjustifiedPoints)
1214
+{
1215
+    if(!canPerformGameAction())
1216
+        return;
1217
+    if(!getFeature(Otc::GameUnjustifiedPoints))
1218
+        return;
1219
+    if(m_unjustifiedPoints == unjustifiedPoints)
1220
+        return;
1221
+
1222
+    m_unjustifiedPoints = unjustifiedPoints;
1223
+    g_lua.callGlobalField("g_game", "onUnjustifiedPointsChange", unjustifiedPoints);
1224
+}
1225
+
1226
+void Game::setOpenPvpSituations(int openPvpSituations)
1227
+{
1228
+    if(!canPerformGameAction())
1229
+        return;
1230
+    if(m_openPvpSituations == openPvpSituations)
1231
+        return;
1232
+
1233
+    m_openPvpSituations = openPvpSituations;
1234
+    g_lua.callGlobalField("g_game", "onOpenPvpSituationsChange", openPvpSituations);
1235
+}
1236
+
1237
+
1207 1238
 void Game::inspectNpcTrade(const ItemPtr& item)
1208 1239
 {
1209 1240
     if(!canPerformGameAction() || !item)
@@ -1425,7 +1456,7 @@ void Game::setProtocolVersion(int version)
1425 1456
     if(isOnline())
1426 1457
         stdext::throw_exception("Unable to change protocol version while online");
1427 1458
 
1428
-    if(version != 0 && (version < 740 || version > 1071))
1459
+    if(version != 0 && (version < 740 || version > 1072))
1429 1460
         stdext::throw_exception(stdext::format("Protocol version %d not supported", version));
1430 1461
 
1431 1462
     m_protocolVersion = version;
@@ -1443,7 +1474,7 @@ void Game::setClientVersion(int version)
1443 1474
     if(isOnline())
1444 1475
         stdext::throw_exception("Unable to change client version while online");
1445 1476
 
1446
-    if(version != 0 && (version < 740 || version > 1071))
1477
+    if(version != 0 && (version < 740 || version > 1072))
1447 1478
         stdext::throw_exception(stdext::format("Client version %d not supported", version));
1448 1479
 
1449 1480
     m_features.reset();
@@ -1563,6 +1594,10 @@ void Game::setClientVersion(int version)
1563 1594
         enableFeature(Otc::GameEnhancedAnimations);
1564 1595
     }
1565 1596
 
1597
+    if(version >= 1053) {
1598
+        enableFeature(Otc::GameUnjustifiedPoints);
1599
+    }
1600
+
1566 1601
     if(version >= 1054) {
1567 1602
         enableFeature(Otc::GameExperienceBonus);
1568 1603
     }
@@ -1575,6 +1610,10 @@ void Game::setClientVersion(int version)
1575 1610
         enableFeature(Otc::GameContentRevision);
1576 1611
     }
1577 1612
 
1613
+    if(version >= 1072) {
1614
+        enableFeature(Otc::GameAuthenticator);
1615
+    }
1616
+
1578 1617
     m_clientVersion = version;
1579 1618
 
1580 1619
     g_lua.callGlobalField("g_game", "onClientVersionChange", version);

+ 32
- 1
src/client/game.h View File

@@ -36,6 +36,25 @@
36 36
 
37 37
 #include <bitset>
38 38
 
39
+struct UnjustifiedPoints {
40
+    bool operator==(const UnjustifiedPoints& other) {
41
+        return killsDay == other.killsDay &&
42
+            killsDayRemaining == other.killsDayRemaining &&
43
+            killsWeek == other.killsWeek &&
44
+            killsWeekRemaining == other.killsWeekRemaining &&
45
+            killsMonth == other.killsMonth &&
46
+            killsMonthRemaining == other.killsMonthRemaining &&
47
+            skullTime == other.skullTime;
48
+    }
49
+    uint8 killsDay;
50
+    uint8 killsDayRemaining;
51
+    uint8 killsWeek;
52
+    uint8 killsWeekRemaining;
53
+    uint8 killsMonth;
54
+    uint8 killsMonthRemaining;
55
+    uint8 skullTime;
56
+};
57
+
39 58
 typedef std::tuple<std::string, uint, std::string, int, bool> Vip;
40 59
 
41 60
 //@bindsingleton g_game
@@ -60,6 +79,7 @@ protected:
60 79
     void processLoginError(const std::string& error);
61 80
     void processLoginAdvice(const std::string& message);
62 81
     void processLoginWait(const std::string& message, int time);
82
+    void processLoginToken(bool unknown);
63 83
     void processLogin();
64 84
     void processPendingGame();
65 85
     void processEnterGame();
@@ -139,7 +159,7 @@ protected:
139 159
 
140 160
 public:
141 161
     // login related
142
-    void loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName);
162
+    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);
143 163
     void cancelLogin();
144 164
     void forceLogout();
145 165
     void safeLogout();
@@ -218,6 +238,12 @@ public:
218 238
     bool isSafeFight() { return m_safeFight; }
219 239
     Otc::PVPModes getPVPMode() { return m_pvpMode; }
220 240
 
241
+    // pvp related
242
+    void setUnjustifiedPoints(UnjustifiedPoints unjustifiedPoints);
243
+    UnjustifiedPoints getUnjustifiedPoints() { return m_unjustifiedPoints; };
244
+    void setOpenPvpSituations(int openPvpSitations);
245
+    int getOpenPvpSituations() { return m_openPvpSituations; }
246
+
221 247
     // npc trade related
222 248
     void inspectNpcTrade(const ItemPtr& item);
223 249
     void buyItem(const ItemPtr& item, int amount, bool ignoreCapacity, bool buyWithBackpack);
@@ -304,6 +330,8 @@ public:
304 330
     int getServerBeat() { return m_serverBeat; }
305 331
     void setCanReportBugs(bool enable) { m_canReportBugs = enable; }
306 332
     bool canReportBugs() { return m_canReportBugs; }
333
+    void setExpertPvpMode(bool enable) { m_expertPvpMode = enable; }
334
+    bool getExpertPvpMode() { return m_expertPvpMode; }
307 335
     LocalPlayerPtr getLocalPlayer() { return m_localPlayer; }
308 336
     ProtocolGamePtr getProtocolGame() { return m_protocolGame; }
309 337
     std::string getCharacterName() { return m_characterName; }
@@ -333,6 +361,7 @@ private:
333 361
     bool m_online;
334 362
     bool m_denyBotCall;
335 363
     bool m_dead;
364
+    bool m_expertPvpMode;
336 365
     int m_serverBeat;
337 366
     ticks_t m_ping;
338 367
     uint m_pingSent;
@@ -345,6 +374,8 @@ private:
345 374
     Otc::ChaseModes m_chaseMode;
346 375
     Otc::PVPModes m_pvpMode;
347 376
     Otc::Direction m_lastWalkDir;
377
+    UnjustifiedPoints m_unjustifiedPoints;
378
+    int m_openPvpSituations;
348 379
     bool m_safeFight;
349 380
     bool m_canReportBugs;
350 381
     std::vector<uint8> m_gmActions;

+ 4
- 0
src/client/luafunctions.cpp View File

@@ -238,6 +238,10 @@ void Client::registerLuaFunctions()
238 238
     g_lua.bindSingletonFunction("g_game", "getChaseMode", &Game::getChaseMode, &g_game);
239 239
     g_lua.bindSingletonFunction("g_game", "getFightMode", &Game::getFightMode, &g_game);
240 240
     g_lua.bindSingletonFunction("g_game", "getPVPMode", &Game::getPVPMode, &g_game);
241
+    g_lua.bindSingletonFunction("g_game", "setUnjustifiedPoints", &Game::setUnjustifiedPoints, &g_game);
242
+    g_lua.bindSingletonFunction("g_game", "getUnjustifiedPoints", &Game::getUnjustifiedPoints, &g_game);
243
+    g_lua.bindSingletonFunction("g_game", "setOpenPvpSituations", &Game::setOpenPvpSituations, &g_game);
244
+    g_lua.bindSingletonFunction("g_game", "getOpenPvpSituations", &Game::getOpenPvpSituations, &g_game);
241 245
     g_lua.bindSingletonFunction("g_game", "isSafeFight", &Game::isSafeFight, &g_game);
242 246
     g_lua.bindSingletonFunction("g_game", "inspectNpcTrade", &Game::inspectNpcTrade, &g_game);
243 247
     g_lua.bindSingletonFunction("g_game", "buyItem", &Game::buyItem, &g_game);

+ 42
- 0
src/client/luavaluecasts.cpp View File

@@ -165,3 +165,45 @@ bool luavalue_cast(int index, Light& light)
165 165
     }
166 166
     return false;
167 167
 }
168
+
169
+int push_luavalue(const UnjustifiedPoints& unjustifiedPoints)
170
+{
171
+    g_lua.createTable(0, 7);
172
+    g_lua.pushInteger(unjustifiedPoints.killsDay);
173
+    g_lua.setField("killsDay");
174
+    g_lua.pushInteger(unjustifiedPoints.killsDayRemaining);
175
+    g_lua.setField("killsDayRemaining");
176
+    g_lua.pushInteger(unjustifiedPoints.killsWeek);
177
+    g_lua.setField("killsWeek");
178
+    g_lua.pushInteger(unjustifiedPoints.killsWeekRemaining);
179
+    g_lua.setField("killsWeekRemaining");
180
+    g_lua.pushInteger(unjustifiedPoints.killsMonth);
181
+    g_lua.setField("killsMonth");
182
+    g_lua.pushInteger(unjustifiedPoints.killsMonthRemaining);
183
+    g_lua.setField("killsMonthRemaining");
184
+    g_lua.pushInteger(unjustifiedPoints.skullTime);
185
+    g_lua.setField("skullTime");
186
+    return 1;
187
+}
188
+
189
+bool luavalue_cast(int index, UnjustifiedPoints& unjustifiedPoints)
190
+{
191
+    if(g_lua.isTable(index)) {
192
+        g_lua.getField("killsDay", index);
193
+        unjustifiedPoints.killsDay = g_lua.popInteger();
194
+        g_lua.getField("killsDayRemaining", index);
195
+        unjustifiedPoints.killsDayRemaining = g_lua.popInteger();
196
+        g_lua.getField("killsWeek", index);
197
+        unjustifiedPoints.killsWeek = g_lua.popInteger();
198
+        g_lua.getField("killsWeekRemaining", index);
199
+        unjustifiedPoints.killsWeekRemaining = g_lua.popInteger();
200
+        g_lua.getField("killsMonth", index);
201
+        unjustifiedPoints.killsMonth = g_lua.popInteger();
202
+        g_lua.getField("killsMonthRemaining", index);
203
+        unjustifiedPoints.killsMonthRemaining = g_lua.popInteger();
204
+        g_lua.getField("skullTime", index);
205
+        unjustifiedPoints.skullTime = g_lua.popInteger();
206
+        return true;
207
+    }
208
+    return false;
209
+}

+ 4
- 0
src/client/luavaluecasts.h View File

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

+ 1
- 0
src/client/protocolcodes.h View File

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

+ 2
- 1
src/client/protocolgame.cpp View File

@@ -26,10 +26,11 @@
26 26
 #include "item.h"
27 27
 #include "localplayer.h"
28 28
 
29
-void ProtocolGame::login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName)
29
+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)
30 30
 {
31 31
     m_accountName = accountName;
32 32
     m_accountPassword = accountPassword;
33
+    m_authenticatorToken = authenticatorToken;
33 34
     m_characterName = characterName;
34 35
 
35 36
     connect(host, port);

+ 3
- 1
src/client/protocolgame.h View File

@@ -31,7 +31,7 @@
31 31
 class ProtocolGame : public Protocol
32 32
 {
33 33
 public:
34
-    void login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName);
34
+    void login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName, const std::string& authenticatorToken);
35 35
     void send(const OutputMessagePtr& outputMessage);
36 36
 
37 37
     void sendExtendedOpcode(uint8 opcode, const std::string& buffer);
@@ -143,6 +143,7 @@ private:
143 143
     void parseLoginError(const InputMessagePtr& msg);
144 144
     void parseLoginAdvice(const InputMessagePtr& msg);
145 145
     void parseLoginWait(const InputMessagePtr& msg);
146
+    void parseLoginToken(const InputMessagePtr& msg);
146 147
     void parsePing(const InputMessagePtr& msg);
147 148
     void parsePingBack(const InputMessagePtr& msg);
148 149
     void parseChallenge(const InputMessagePtr& msg);
@@ -246,6 +247,7 @@ private:
246 247
     stdext::boolean<true> m_firstRecv;
247 248
     std::string m_accountName;
248 249
     std::string m_accountPassword;
250
+    std::string m_authenticatorToken;
249 251
     std::string m_characterName;
250 252
     LocalPlayerPtr m_localPlayer;
251 253
 };

+ 27
- 12
src/client/protocolgameparse.cpp View File

@@ -79,6 +79,9 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
79 79
             case Proto::GameServerLoginWait:
80 80
                 parseLoginWait(msg);
81 81
                 break;
82
+            case Proto::GameServerLoginToken:
83
+                parseLoginToken(msg);
84
+                break;
82 85
             case Proto::GameServerPing:
83 86
             case Proto::GameServerPingBack:
84 87
                 if((opcode == Proto::GameServerPing && g_game.getFeature(Otc::GameClientPing)) ||
@@ -385,11 +388,13 @@ void ProtocolGame::parseLogin(const InputMessagePtr& msg)
385 388
     }
386 389
     bool canReportBugs = msg->getU8();
387 390
 
388
-    if(g_game.getClientVersion() >= 1053)
391
+    if(g_game.getClientVersion() >= 1054)
389 392
         msg->getU8(); // can change pvp frame option
390 393
 
391
-    if(g_game.getClientVersion() >= 1058)
392
-        msg->getU8(); // expert mode enabled
394
+    if(g_game.getClientVersion() >= 1058) {
395
+        int expertModeEnabled = msg->getU8();
396
+        g_game.setExpertPvpMode(expertModeEnabled);
397
+    }
393 398
 
394 399
     m_localPlayer->setId(playerId);
395 400
     g_game.setServerBeat(serverBeat);
@@ -428,19 +433,23 @@ void ProtocolGame::parsePreset(const InputMessagePtr& msg)
428 433
 
429 434
 void ProtocolGame::parseUnjustifiedStats(const InputMessagePtr& msg)
430 435
 {
431
-    // Unjustified Kills display since 10.55
432
-    msg->getU8();
433
-    msg->getU8();
434
-    msg->getU8();
435
-    msg->getU8();
436
-    msg->getU8();
437
-    msg->getU8();
438
-    msg->getU8();
436
+    UnjustifiedPoints unjustifiedPoints;
437
+    unjustifiedPoints.killsDay = msg->getU8();
438
+    unjustifiedPoints.killsDayRemaining = msg->getU8();
439
+    unjustifiedPoints.killsWeek = msg->getU8();
440
+    unjustifiedPoints.killsWeekRemaining = msg->getU8();
441
+    unjustifiedPoints.killsMonth = msg->getU8();
442
+    unjustifiedPoints.killsMonthRemaining = msg->getU8();
443
+    unjustifiedPoints.skullTime = msg->getU8();
444
+
445
+    g_game.setUnjustifiedPoints(unjustifiedPoints);
439 446
 }
440 447
 
441 448
 void ProtocolGame::parsePvpSituations(const InputMessagePtr& msg)
442 449
 {
443
-    msg->getU8(); // amount of open pvp situations
450
+    uint8 openPvpSituations = msg->getU8();
451
+
452
+    g_game.setOpenPvpSituations(openPvpSituations);
444 453
 }
445 454
 
446 455
 void ProtocolGame::parsePlayerHelpers(const InputMessagePtr& msg)
@@ -501,6 +510,12 @@ void ProtocolGame::parseLoginWait(const InputMessagePtr& msg)
501 510
     g_game.processLoginWait(message, time);
502 511
 }
503 512
 
513
+void ProtocolGame::parseLoginToken(const InputMessagePtr& msg)
514
+{
515
+    bool unknown = (msg->getU8() == 0);
516
+    g_game.processLoginToken(unknown);
517
+}
518
+
504 519
 void ProtocolGame::parsePing(const InputMessagePtr& msg)
505 520
 {
506 521
     g_game.processPing();

+ 3
- 0
src/client/protocolgamesend.cpp View File

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

Loading…
Cancel
Save