Multi-protocol

Lots of chagnes to add multi protocol flexibility, not really
completed yet, still have to rework text messages opcodes and other stuff,
so this still a working in progress feature

* Rework dat reader, the dat reader can now
* dinamically detect dat version
* Split game into gamelib and game_interface
* Lots of other minor changes
This commit is contained in:
Eduardo Bart
2012-07-17 20:49:21 -03:00
parent 6fc11d2fa9
commit eb24d6776e
64 changed files with 536 additions and 588 deletions

83
modules/gamelib/const.lua Normal file
View File

@@ -0,0 +1,83 @@
-- @docconsts @{
SkullNone = 0
SkullYellow = 1
SkullGreen = 2
SkullWhite = 3
SkullRed = 4
SkullBlack = 5
SkullOrange = 6
ShieldNone = 0
ShieldWhiteYellow = 1
ShieldWhiteBlue = 2
ShieldBlue = 3
ShieldYellow = 4
ShieldBlueSharedExp = 5
ShieldYellowSharedExp = 6
ShieldBlueNoSharedExpBlink = 7
ShieldYellowNoSharedExpBlink = 8
ShieldBlueNoSharedExp = 9
ShieldYellowNoSharedExp = 10
EmblemNone = 0
EmblemGreen = 1
EmblemRed = 2
EmblemBlue = 3
North = 0
East = 1
South = 2
West = 3
NorthEast = 4
SouthEast = 5
SouthWest = 6
NorthWest = 7
GameExtendedOpcode = 0
GameProtocolChecksum = 1
GameAccountNames = 2
GameChallangeOnLogin = 3
GameStackposOnTileAddThing = 4
GamePenalityOnDeath = 5
GameNameOnNpcTrade = 6
GameDoubleFreeCapacity = 7
GameDoubleExperience = 8
GameTotalCapacity = 9
GameSkillsBase = 10
GameAdditionalPlayerStats = 11
GameIdOnCancelAttack = 12
GameChannelPlayerList = 13
GamePlayerMounts = 14
GameEnvironmentEffect = 15
GameCreatureType = 16
GameCreatureEmblems = 17
GameCreaturePassableInfo = 18
GameItemAnimationPhase = 19
GameTrucatedPingOpcode = 20
GameReverseCreatureStack = 21
GameMagicEffectU16 = 22
GamePlayerMarket = 23
OTSERV_RSA = "1091201329673994292788609605089955415282375029027981291234687579" ..
"3726629149257644633073969600111060390723088861007265581882535850" ..
"3429057592827629436413108566029093628212635953836686562675849720" ..
"6207862794310902180176810615217550567108238764764442605581471797" ..
"07119674283982419152118103759076030616683978566631413"
CIPSOFT_RSA = "1321277432058722840622950990822933849527763264961655079678763618" ..
"4334395343554449668205332383339435179772895415509701210392836078" ..
"6959821132214473291575712138800495033169914814069637740318278150" ..
"2907336840325241747827401343576296990629870233111328210165697754" ..
"88792221429527047321331896351555606801473202394175817"
OsTypes = {
Linux = 1,
Windows = 2,
Flash = 3,
OtclientLinux = 10,
OtclientWindows = 11,
OtclientMac = 12
}
-- @}

View File

@@ -0,0 +1,110 @@
-- @docclass Creature
-- @docconsts @{
SkullNone = 0
SkullYellow = 1
SkullGreen = 2
SkullWhite = 3
SkullRed = 4
SkullBlack = 5
SkullOrange = 6
ShieldNone = 0
ShieldWhiteYellow = 1
ShieldWhiteBlue = 2
ShieldBlue = 3
ShieldYellow = 4
ShieldBlueSharedExp = 5
ShieldYellowSharedExp = 6
ShieldBlueNoSharedExpBlink = 7
ShieldYellowNoSharedExpBlink = 8
ShieldBlueNoSharedExp = 9
ShieldYellowNoSharedExp = 10
EmblemNone = 0
EmblemGreen = 1
EmblemRed = 2
EmblemBlue = 3
-- @}
function getSkullImagePath(skullId)
local path
if skullId == SkullYellow then
path = 'icons/skull_yellow.png'
elseif skullId == SkullGreen then
path = 'icons/skull_green.png'
elseif skullId == SkullWhite then
path = 'icons/skull_white.png'
elseif skullId == SkullRed then
path = 'icons/skull_red.png'
elseif skullId == SkullBlack then
path = 'icons/skull_black.png'
elseif skullId == SkullOrange then
path = 'icons/skull_orange.png'
end
path = resolvepath(path)
return path
end
function getShieldImagePathAndBlink(shieldId)
local path
if shieldId == ShieldWhiteYellow then
path = 'icons/shield_yellow_white.png', false
elseif shieldId == ShieldWhiteBlue then
path = 'icons/shield_blue_white.png', false
elseif shieldId == ShieldBlue then
path = 'icons/shield_blue.png', false
elseif shieldId == ShieldYellow then
path = 'icons/shield_yellow.png', false
elseif shieldId == ShieldBlueSharedExp then
path = 'icons/shield_blue_shared.png', false
elseif shieldId == ShieldYellowSharedExp then
path = 'icons/shield_yellow_shared.png', false
elseif shieldId == ShieldBlueNoSharedExpBlink then
path = 'icons/shield_blue_not_shared.png', true
elseif shieldId == ShieldYellowNoSharedExpBlink then
path = 'icons/shield_yellow_not_shared.png', true
elseif shieldId == ShieldBlueNoSharedExp then
path = 'icons/shield_blue_not_shared.png', false
elseif shieldId == ShieldYellowNoSharedExp then
path = 'icons/shield_yellow_not_shared.png', false
end
path = resolvepath(path)
return path
end
function getEmblemImagePath(emblemId)
local path
if emblemId == EmblemGreen then
path = 'icons/emblem_green.png'
elseif emblemId == EmblemRed then
path = 'icons/emblem_red.png'
elseif emblemId == EmblemBlue then
path = 'icons/emblem_blue.png'
end
path = resolvepath(path)
return path
end
function Creature:onSkullChange(skullId)
local imagePath = getSkullImagePath(skullId)
if imagePath then
self:setSkullTexture(imagePath)
end
end
function Creature:onShieldChange(shieldId)
local imagePath, blink = getShieldImagePathAndBlink(shieldId)
if imagePath then
self:setShieldTexture(imagePath, blink)
end
end
function Creature:onEmblemChange(emblemId)
local imagePath = getEmblemImagePath(emblemId)
if imagePath then
self:setEmblemTexture(imagePath)
end
end

47
modules/gamelib/game.lua Normal file
View File

@@ -0,0 +1,47 @@
local currentRsa = OTSERV_RSA
function g_game.getRsa()
return currentRsa
end
function g_game.chooseRsa(host)
if host:match('.*\.tibia\.com') or host:match('.*\.cipsoft\.com') then
currentRsa = CIPSOFT_RSA
else
currentRsa = OTSERV_RSA
end
end
function g_game.setRsa(rsa)
currentRsa = rsa
end
function g_game.isOfficialTibia()
return currentRsa == CIPSOFT_RSA
end
function g_game.getOsType()
if g_game.isOfficialTibia() then
if g_app.getOs() == 'windows' then
return OsTypes.Windows
else
return OsTypes.Linux
end
else
if g_app.getOs() == 'windows' then
return OsTypes.OtclientWindows
elseif g_app.getOs() == 'mac' then
return OsTypes.OtclientMac
else
return OsTypes.OtclientLinux
end
end
end
function g_game.getSupportedProtocols()
return {
810, 853, 854, 860, 861, 862, 870, 940,
953, 954, 960
}
end

View File

@@ -0,0 +1,20 @@
Module
name: gamelib
description: Contains game related classes
author: OTClient team
website: www.otclient.info
dependencies:
- client_extended
- game_tibiafiles
@onLoad: |
dofile 'const'
dofile 'protocollogin'
dofile 'protocolgame'
dofile 'game'
dofile 'creature'
dofile 'player'
dofile 'market'

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

View File

@@ -0,0 +1,38 @@
MarketAction = {
Buy = 0,
Sell = 1
}
MarketRequest = {
MyOffers = 0xFFFE,
MyHistory = 0xFFFF
}
MarketOfferState = {
Active = 0,
Cancelled = 1,
Expired = 2,
Accepted = 3,
AcceptedEx = 255
}
MarketItemDescription = {
Armor = 1,
Attack = 2,
Container = 3,
Defense = 4,
General = 5,
DecayTime = 6,
Combat = 7,
MinLevel = 8,
MinMagicLevel = 9,
Vocation = 10,
Rune = 11,
Ability = 12,
Charges = 13,
WeaponName = 14,
Weight = 15,
First = Armor,
Last = Weight
}

View File

@@ -0,0 +1,54 @@
-- @docclass Player
InventorySlotHead = 1
InventorySlotNeck = 2
InventorySlotBack = 3
InventorySlotBody = 4
InventorySlotRight = 5
InventorySlotLeft = 6
InventorySlotLeg = 7
InventorySlotFeet = 8
InventorySlotFinger = 9
InventorySlotAmmo = 10
InventorySlotFirst = 1
InventorySlotLast = 10
function Player:isPartyLeader()
local shield = self:getShield()
return (shield == ShieldWhiteYellow or
shield == ShieldYellow or
shield == ShieldYellowSharedExp or
shield == ShieldYellowNoSharedExpBlink or
shield == ShieldYellowNoSharedExp)
end
function Player:isPartyMember()
local shield = self:getShield()
return (shield == ShieldWhiteYellow or
shield == ShieldYellow or
shield == ShieldYellowSharedExp or
shield == ShieldYellowNoSharedExpBlink or
shield == ShieldYellowNoSharedExp or
shield == ShieldBlueSharedExp or
shield == ShieldBlueNoSharedExpBlink or
shield == ShieldBlueNoSharedExp or
shield == ShieldBlue)
end
function Player:isPartySharedExperienceActive()
local shield = self:getShield()
return (shield == ShieldYellowSharedExp or
shield == ShieldYellowNoSharedExpBlink or
shield == ShieldYellowNoSharedExp or
shield == ShieldBlueSharedExp or
shield == ShieldBlueNoSharedExpBlink or
shield == ShieldBlueNoSharedExp)
end
function Player:hasVip(creatureName)
for id, vip in pairs(g_game.getVips()) do
if (vip[1] == creatureName) then return true end
end
return false
end

View File

@@ -0,0 +1,23 @@
local opcodeCallbacks = {}
function ProtocolGame:onOpcode(opcode, msg)
for i, callback in pairs(opcodeCallbacks) do
if i == opcode then
callback(msg)
return true
end
end
return false
end
function ProtocolGame.registerOpcode(opcode, callback)
if opcodeCallbacks[opcode] then
error('opcode ' .. opcode .. ' already registered will be overriden')
end
opcodeCallbacks[opcode] = callback
end
function ProtocolGame.unregisterOpcode(opcode)
opcodeCallbacks[opcode] = nil
end

View File

@@ -0,0 +1,135 @@
-- @docclass
ProtocolLogin = extends(Protocol)
-- set to the latest Tibia.pic signature to make otclient compatible with official tibia
local PIC_SIGNATURE = 0
LoginServerError = 10
LoginServerMotd = 20
LoginServerUpdateNeeded = 30
LoginServerCharacterList = 100
-- private functions
local function sendLoginPacket(protocol)
local msg = OutputMessage.create()
msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU16(g_game.getOsType())
msg:addU16(g_game.getProtocolVersion())
msg:addU32(g_things.getDatSignature())
msg:addU32(g_sprites.getSprSignature())
msg:addU32(PIC_SIGNATURE)
local paddingBytes = 128
msg:addU8(0) -- first RSA byte must be 0
paddingBytes = paddingBytes - 1
-- xtea key
protocol:generateXteaKey()
local xteaKey = protocol:getXteaKey()
msg:addU32(xteaKey[1])
msg:addU32(xteaKey[2])
msg:addU32(xteaKey[3])
msg:addU32(xteaKey[4])
paddingBytes = paddingBytes - 16
if g_game.getFeature(GameProtocolChecksum) then
protocol:enableChecksum()
end
if g_game.getFeature(GameAccountNames) then
msg:addString(protocol.accountName)
msg:addString(protocol.accountPassword)
paddingBytes = paddingBytes - (4 + string.len(protocol.accountName) + string.len(protocol.accountPassword))
else
msg:addU32(tonumber(protocol.accountName))
msg:addString(protocol.accountPassword)
paddingBytes = paddingBytes - (6 + string.len(protocol.accountPassword))
end
msg:addPaddingBytes(paddingBytes, 0)
msg:encryptRsa(128, g_game.getRsa())
protocol:send(msg)
protocol:enableXteaEncryption()
protocol:recv()
end
-- events
function ProtocolLogin:onConnect()
self:connectCallback(self)
end
function ProtocolLogin:onRecv(msg)
while not msg:eof() do
local opcode = msg:getU8()
if opcode == LoginServerError then
self:parseError(msg)
elseif opcode == LoginServerMotd then
self:parseMotd(msg)
elseif opcode == LoginServerUpdateNeeded then
signalcall(self.onError, self, tr("Client needs update."))
elseif opcode == LoginServerCharacterList then
self:parseCharacterList(msg)
else
self:parseOpcode(opcode, msg)
end
end
self:disconnect()
end
-- public functions
function ProtocolLogin.create()
return ProtocolLogin.internalCreate()
end
function ProtocolLogin:login(host, port, accountName, accountPassword)
if string.len(accountName) == 0 or string.len(accountPassword) == 0 then
signalcall(self.onError, self, tr("You must enter an account name and password."))
return
end
if string.len(host) == 0 or port == nil or port == 0 then
signalcall(self.onError, self, tr("You must enter a valid server address and port."))
return
end
self.accountName = accountName
self.accountPassword = accountPassword
self.connectCallback = sendLoginPacket
self:connect(host, port)
end
function ProtocolLogin:cancelLogin()
self:disconnect()
end
function ProtocolLogin:parseError(msg)
local errorMessage = msg:getString()
signalcall(self.onError, self, errorMessage)
end
function ProtocolLogin:parseMotd(msg)
local motd = msg:getString()
signalcall(self.onMotd, self, motd)
end
function ProtocolLogin:parseCharacterList(msg)
local characters = {}
local charactersCount = msg:getU8()
for i=1,charactersCount do
local character = {}
character[1] = msg:getString()
character[2] = msg:getString()
character[3] = iptostring(msg:getU32())
character[4] = msg:getU16()
characters[i] = character
end
local premDays = msg:getU16()
signalcall(self.onCharacterList, self, characters, premDays)
end
function ProtocolLogin:parseOpcode(opcode, msg)
signalcall(self.onOpcode, self, opcode, msg)
end