Support for Protocols up to 10.71, Adventurer Blessing

This commit is contained in:
TheSumm 2014-12-29 18:08:13 +01:00
parent 6ab69b499d
commit 7f3f18f991
21 changed files with 342 additions and 88 deletions

View File

@ -45,13 +45,17 @@ CharacterWidget < UIWidget
MainWindow MainWindow
id: charactersWindow id: charactersWindow
!text: tr('Character List') !text: tr('Character List')
size: 250 248
visible: false visible: false
@onEnter: CharacterList.doLogin() @onEnter: CharacterList.doLogin()
@onEscape: CharacterList.hide(true) @onEscape: CharacterList.hide(true)
@onSetup: | @onSetup: |
g_keyboard.bindKeyPress('Up', function() self:getChildById('characters'):focusPreviousChild(KeyboardFocusReason) end, self) g_keyboard.bindKeyPress('Up', function() self:getChildById('characters'):focusPreviousChild(KeyboardFocusReason) end, self)
g_keyboard.bindKeyPress('Down', function() self:getChildById('characters'):focusNextChild(KeyboardFocusReason) end, self) g_keyboard.bindKeyPress('Down', function() self:getChildById('characters'):focusNextChild(KeyboardFocusReason) end, self)
if g_game.getFeature(GamePreviewState) then
self:setSize({width = 350, height = 400})
else
self:setSize({width = 250, height = 248})
end
TextList TextList
id: characters id: characters

View File

@ -59,6 +59,12 @@ local function onCharacterList(protocol, characters, account, otui)
loadBox:destroy() loadBox:destroy()
loadBox = nil loadBox = nil
for _, characterInfo in pairs(characters) do
if characterInfo.previewState and characterInfo.previewState ~= PreviewState.Default then
characterInfo.worldName = characterInfo.worldName .. ', Preview'
end
end
CharacterList.create(characters, account, otui) CharacterList.create(characters, account, otui)
CharacterList.show() CharacterList.show()
@ -105,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 = 860 end if clientVersion == 0 then clientVersion = 1071 end
if port == nil or port == 0 then port = 7171 end if port == nil or port == 0 then port = 7171 end

View File

@ -17,7 +17,10 @@ inventoryButton = nil
purseButton = nil purseButton = nil
function init() function init()
connect(LocalPlayer, { onInventoryChange = onInventoryChange }) connect(LocalPlayer, {
onInventoryChange = onInventoryChange,
onBlessingsChange = onBlessingsChange
})
connect(g_game, { onGameStart = refresh }) connect(g_game, { onGameStart = refresh })
g_keyboard.bindKeyDown('Ctrl+I', toggle) g_keyboard.bindKeyDown('Ctrl+I', toggle)
@ -43,7 +46,10 @@ function init()
end end
function terminate() function terminate()
disconnect(LocalPlayer, { onInventoryChange = onInventoryChange }) disconnect(LocalPlayer, {
onInventoryChange = onInventoryChange,
onBlessingsChange = onBlessingsChange
})
disconnect(g_game, { onGameStart = refresh }) disconnect(g_game, { onGameStart = refresh })
g_keyboard.unbindKeyDown('Ctrl+I') g_keyboard.unbindKeyDown('Ctrl+I')
@ -52,6 +58,21 @@ function terminate()
inventoryButton:destroy() inventoryButton:destroy()
end end
function toggleAdventurerStyle(hasBlessing)
for slot = InventorySlotFirst, InventorySlotLast do
local itemWidget = inventoryPanel:getChildById('slot' .. slot)
if itemWidget then
if hasBlessing then
itemWidget:setBorderWidth(1)
itemWidget:setBorderColor('#F7C80C')
else
itemWidget:setBorderWidth(0)
itemWidget:setBorderColor('white')
end
end
end
end
function refresh() function refresh()
local player = g_game.getLocalPlayer() local player = g_game.getLocalPlayer()
for i = InventorySlotFirst, InventorySlotPurse do for i = InventorySlotFirst, InventorySlotPurse do
@ -60,6 +81,7 @@ function refresh()
else else
onInventoryChange(player, i, nil) onInventoryChange(player, i, nil)
end end
toggleAdventurerStyle(Bit.hasBit(player:getBlessings(), Blessings.Adventurer))
end end
purseButton:setVisible(g_game.getFeature(GamePurseSlot)) purseButton:setVisible(g_game.getFeature(GamePurseSlot))
@ -99,3 +121,10 @@ function onInventoryChange(player, slot, item, oldItem)
itemWidget:setItem(nil) itemWidget:setItem(nil)
end end
end end
function onBlessingsChange(player, blessings, oldBlessings)
local hasAdventurerBlessing = Bit.hasBit(blessings, Blessings.Adventurer)
if hasAdventurerBlessing ~= Bit.hasBit(oldBlessings, Blessings.Adventurer) then
toggleAdventurerStyle(hasAdventurerBlessing)
end
end

View File

@ -120,6 +120,13 @@ GameSpritesAlphaChannel = 56
GamePremiumExpiration = 57 GamePremiumExpiration = 57
GameBrowseField = 58 GameBrowseField = 58
GameEnhancedAnimations = 59 GameEnhancedAnimations = 59
GameOGLInformation = 60
GameMessageSizeCheck = 61
GamePreviewState = 62
GameLoginPacketEncryption = 63
GameClientVersion = 64
GameContentRevision = 65
GameExperienceBonus = 66
TextColors = { TextColors = {
red = '#f55e5e', --'#c83200' red = '#f55e5e', --'#c83200'
@ -201,7 +208,7 @@ CIPSOFT_RSA = "1321277432058722840622950990822933849527763264961655079678763618"
"88792221429527047321331896351555606801473202394175817" "88792221429527047321331896351555606801473202394175817"
-- set to the latest Tibia.pic signature to make otclient compatible with official tibia -- set to the latest Tibia.pic signature to make otclient compatible with official tibia
PIC_SIGNATURE = 0x53208400 PIC_SIGNATURE = 0x542100C1
OsTypes = { OsTypes = {
Linux = 1, Linux = 1,
@ -244,4 +251,20 @@ ExtendedIds = {
NeedsUpdate = 7 NeedsUpdate = 7
} }
PreviewState = {
Default = 0,
Inactive = 1,
Active = 2
}
Blessings = {
None = 0,
Adventurer = 1,
SpiritualShielding = 2,
EmbraceOfTibia = 4,
FireOfSuns = 8,
WisdomOfSolitude = 16,
SparkOfPhoenix = 32
}
-- @} -- @}

View File

@ -53,38 +53,39 @@ end
function g_game.getSupportedClients() function g_game.getSupportedClients()
return { return {
740, 741, 750, 760, 770, 772, 740, 741, 750, 760, 770, 772,
780, 781, 782, 790, 792, 780, 781, 782, 790, 792,
800, 810, 811, 820, 821, 822, 800, 810, 811, 820, 821, 822,
830, 831, 840, 842, 850, 853, 830, 831, 840, 842, 850, 853,
854, 855, 857, 860, 861, 862, 854, 855, 857, 860, 861, 862,
870, 871, 870, 871,
900, 910, 920, 931, 940, 943, 900, 910, 920, 931, 940, 943,
944, 951, 952, 953, 954, 960, 944, 951, 952, 953, 954, 960,
961, 963, 970, 971, 972, 973, 961, 963, 970, 971, 972, 973,
980, 981, 982, 983, 984, 985, 980, 981, 982, 983, 984, 985,
986, 986,
1000, 1001, 1002, 1010, 1011, 1000, 1001, 1002, 1010, 1011,
1012, 1013, 1020, 1021, 1022, 1012, 1013, 1020, 1021, 1022,
1030, 1031, 1032, 1033, 1034, 1030, 1031, 1032, 1033, 1034,
1035, 1036, 1037, 1038, 1039, 1035, 1036, 1037, 1038, 1039,
1040, 1041, 1050, 1051, 1052, 1040, 1041, 1050, 1051, 1052,
1053, 1054, 1055, 1056, 1057, 1053, 1054, 1055, 1056, 1057,
1058, 1059, 1060, 1061 1058, 1059, 1060, 1061, 1062,
1063, 1064, 1070, 1071
} }
end end
-- The client version and protocol version where -- The client version and protocol version where
-- unsynchronized for some releases, not sure if this -- unsynchronized for some releases, not sure if this
-- will be the normal standard. -- will be the normal standard.
-- Client Version: Publicly given version when -- Client Version: Publicly given version when
-- downloading Cipsoft client. -- downloading Cipsoft client.
-- Protocol Version: Previously was the same as -- Protocol Version: Previously was the same as
-- the client version, but was unsychronized in some -- the client version, but was unsychronized in some
-- releases, now it needs to be verified and added here -- releases, now it needs to be verified and added here
-- if it does not match the client version. -- if it does not match the client version.
@ -92,7 +93,7 @@ end
-- Reason for defining both: The server now requires a -- Reason for defining both: The server now requires a
-- Client version and Protocol version from the client. -- Client version and Protocol version from the client.
-- Important: Use getClientVersion for specific protocol -- Important: Use getClientVersion for specific protocol
-- features to ensure we are using the proper version. -- features to ensure we are using the proper version.
function g_game.getClientProtocolVersion(client) function g_game.getClientProtocolVersion(client)

View File

@ -32,23 +32,28 @@ function ProtocolLogin:sendLoginPacket()
msg:addU16(g_game.getProtocolVersion()) msg:addU16(g_game.getProtocolVersion())
if g_game.getClientVersion() >= 980 then if g_game.getFeature(GameClientVersion) then
msg:addU32(g_game.getClientVersion()) msg:addU32(g_game.getClientVersion())
end end
msg:addU32(g_things.getDatSignature()) if g_game.getFeature(GameContentRevision) then
msg:addU16(g_things.getContentRevision())
msg:addU16(0)
else
msg:addU32(g_things.getDatSignature())
end
msg:addU32(g_sprites.getSprSignature()) msg:addU32(g_sprites.getSprSignature())
msg:addU32(PIC_SIGNATURE) msg:addU32(PIC_SIGNATURE)
if g_game.getClientVersion() >= 980 then if g_game.getFeature(GamePreviewState) then
msg:addU8(0) -- clientType msg:addU8(0)
end end
local offset = msg:getMessageSize() local offset = msg:getMessageSize()
if g_game.getFeature(GameLoginPacketEncryption) then
if g_game.getClientVersion() >= 770 then
-- first RSA byte must be 0 -- first RSA byte must be 0
msg:addU8(0) msg:addU8(0)
-- xtea key -- xtea key
self:generateXteaKey() self:generateXteaKey()
local xteaKey = self:getXteaKey() local xteaKey = self:getXteaKey()
@ -74,16 +79,23 @@ 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) msg:addPaddingBytes(paddingBytes, 0)
if g_game.getClientVersion() >= 770 then if g_game.getFeature(GameLoginPacketEncryption) then
msg:encryptRsa() msg:encryptRsa()
end end
if g_game.getFeature(GameOGLInformation) then
msg:addU8(1) --unknown
msg:addU8(1) --unknown
msg:addString(g_graphics.getRenderer())
msg:addString(g_graphics.getVersion())
end
if g_game.getFeature(GameProtocolChecksum) then if g_game.getFeature(GameProtocolChecksum) then
self:enableChecksum() self:enableChecksum()
end end
self:send(msg) self:send(msg)
if g_game.getClientVersion() >= 770 then if g_game.getFeature(GameLoginPacketEncryption) then
self:enableXteaEncryption() self:enableXteaEncryption()
end end
self:recv() self:recv()
@ -141,7 +153,7 @@ function ProtocolLogin:parseCharacterList(msg)
world.worldName = msg:getString() world.worldName = msg:getString()
world.worldIp = msg:getString() world.worldIp = msg:getString()
world.worldPort = msg:getU16() world.worldPort = msg:getU16()
msg:getU8() -- unknow byte? world.previewState = msg:getU8()
worlds[worldId] = world worlds[worldId] = world
end end
@ -153,6 +165,7 @@ function ProtocolLogin:parseCharacterList(msg)
character.worldName = worlds[worldId].worldName character.worldName = worlds[worldId].worldName
character.worldIp = worlds[worldId].worldIp character.worldIp = worlds[worldId].worldIp
character.worldPort = worlds[worldId].worldPort character.worldPort = worlds[worldId].worldPort
character.previewState = worlds[worldId].previewState
characters[i] = character characters[i] = character
end end
@ -165,8 +178,8 @@ function ProtocolLogin:parseCharacterList(msg)
character.worldIp = iptostring(msg:getU32()) character.worldIp = iptostring(msg:getU32())
character.worldPort = msg:getU16() character.worldPort = msg:getU16()
if g_game.getClientVersion() >= 980 then if g_game.getFeature(GamePreviewState) then
character.unknown = msg:getU8() character.previewState = msg:getU8()
end end
characters[i] = character characters[i] = character

View File

@ -328,7 +328,8 @@ namespace Otc
MessageRVRChannel = 46, MessageRVRChannel = 46,
MessageRVRAnswer = 47, MessageRVRAnswer = 47,
MessageRVRContinue = 48, MessageRVRContinue = 48,
LastMessage = 49, MessageGameHighlight = 49,
LastMessage = 50,
MessageInvalid = 255 MessageInvalid = 255
}; };
@ -390,6 +391,13 @@ namespace Otc
GamePremiumExpiration = 57, GamePremiumExpiration = 57,
GameBrowseField = 58, GameBrowseField = 58,
GameEnhancedAnimations = 59, GameEnhancedAnimations = 59,
GameOGLInformation = 60,
GameMessageSizeCheck = 61,
GamePreviewState = 62,
GameLoginPacketEncryption = 63,
GameClientVersion = 64,
GameContentRevision = 65,
GameExperienceBonus = 66,
LastGameFeature = 101 LastGameFeature = 101
}; };
@ -450,6 +458,16 @@ namespace Otc
PhaseRandom = 254, PhaseRandom = 254,
PhaseAsync = 255 PhaseAsync = 255
}; };
enum Blessings {
BlessingNone = 0,
BlessingAdventurer = 1,
BlessingSpiritualShielding = 1 << 1,
BlessingEmbraceOfTibia = 1 << 2,
BlessingFireOfSuns = 1 << 3,
BlessingWisdomOfSolitude = 1 << 4,
BlessingSparkOfPhoenix = 1 << 5
};
} }
#endif #endif

View File

@ -1425,7 +1425,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 > 1051)) if(version != 0 && (version < 740 || version > 1071))
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 +1443,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 > 1051)) if(version != 0 && (version < 740 || version > 1071))
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();
@ -1452,6 +1452,7 @@ void Game::setClientVersion(int version)
if(version >= 770) { if(version >= 770) {
enableFeature(Otc::GameLooktypeU16); enableFeature(Otc::GameLooktypeU16);
enableFeature(Otc::GameMessageStatements); enableFeature(Otc::GameMessageStatements);
enableFeature(Otc::GameLoginPacketEncryption);
} }
if(version >= 780) { if(version >= 780) {
@ -1475,6 +1476,7 @@ void Game::setClientVersion(int version)
if(version >= 841) { if(version >= 841) {
enableFeature(Otc::GameChallengeOnLogin); enableFeature(Otc::GameChallengeOnLogin);
enableFeature(Otc::GameMessageSizeCheck);
} }
if(version >= 854) { if(version >= 854) {
@ -1523,6 +1525,11 @@ void Game::setClientVersion(int version)
enableFeature(Otc::GameAdditionalVipInfo); enableFeature(Otc::GameAdditionalVipInfo);
} }
if(version >= 980) {
enableFeature(Otc::GamePreviewState);
enableFeature(Otc::GameClientVersion);
}
if(version >= 981) { if(version >= 981) {
enableFeature(Otc::GameLoginPending); enableFeature(Otc::GameLoginPending);
enableFeature(Otc::GameNewSpeedLaw); enableFeature(Otc::GameNewSpeedLaw);
@ -1538,7 +1545,7 @@ void Game::setClientVersion(int version)
enableFeature(Otc::GamePVPMode); enableFeature(Otc::GamePVPMode);
} }
if (version >= 1035) { if(version >= 1035) {
enableFeature(Otc::GameDoubleSkills); enableFeature(Otc::GameDoubleSkills);
enableFeature(Otc::GameBaseSkillU16); enableFeature(Otc::GameBaseSkillU16);
} }
@ -1556,6 +1563,18 @@ void Game::setClientVersion(int version)
enableFeature(Otc::GameEnhancedAnimations); enableFeature(Otc::GameEnhancedAnimations);
} }
if(version >= 1054) {
enableFeature(Otc::GameExperienceBonus);
}
if(version >= 1061) {
enableFeature(Otc::GameOGLInformation);
}
if(version >= 1071) {
enableFeature(Otc::GameContentRevision);
}
m_clientVersion = version; m_clientVersion = version;
g_lua.callGlobalField("g_game", "onClientVersionChange", version); g_lua.callGlobalField("g_game", "onClientVersionChange", version);

View File

@ -31,6 +31,7 @@ LocalPlayer::LocalPlayer()
{ {
m_states = 0; m_states = 0;
m_vocation = 0; m_vocation = 0;
m_blessings = Otc::BlessingNone;
m_walkLockExpiration = 0; m_walkLockExpiration = 0;
m_skillsLevel.fill(-1); m_skillsLevel.fill(-1);
@ -546,6 +547,16 @@ void LocalPlayer::setSpells(const std::vector<int>& spells)
} }
} }
void LocalPlayer::setBlessings(int blessings)
{
if(blessings != m_blessings) {
int oldBlessings = m_blessings;
m_blessings = blessings;
callLuaField("onBlessingsChange", blessings, oldBlessings);
}
}
bool LocalPlayer::hasSight(const Position& pos) bool LocalPlayer::hasSight(const Position& pos)
{ {
return m_position.isInRange(pos, g_map.getAwareRange().left - 1, g_map.getAwareRange().top - 1); return m_position.isInRange(pos, g_map.getAwareRange().left - 1, g_map.getAwareRange().top - 1);

View File

@ -63,6 +63,7 @@ public:
void setRegenerationTime(double regenerationTime); void setRegenerationTime(double regenerationTime);
void setOfflineTrainingTime(double offlineTrainingTime); void setOfflineTrainingTime(double offlineTrainingTime);
void setSpells(const std::vector<int>& spells); void setSpells(const std::vector<int>& spells);
void setBlessings(int blessings);
int getStates() { return m_states; } int getStates() { return m_states; }
int getSkillLevel(Otc::Skill skill) { return m_skillsLevel[skill]; } int getSkillLevel(Otc::Skill skill) { return m_skillsLevel[skill]; }
@ -88,6 +89,7 @@ public:
double getOfflineTrainingTime() { return m_offlineTrainingTime; } double getOfflineTrainingTime() { return m_offlineTrainingTime; }
std::vector<int> getSpells() { return m_spells; } std::vector<int> getSpells() { return m_spells; }
ItemPtr getInventoryItem(Otc::InventorySlot inventory) { return m_inventoryItems[inventory]; } ItemPtr getInventoryItem(Otc::InventorySlot inventory) { return m_inventoryItems[inventory]; }
int getBlessings() { return m_blessings; }
bool hasSight(const Position& pos); bool hasSight(const Position& pos);
bool isKnown() { return m_known; } bool isKnown() { return m_known; }
@ -144,6 +146,7 @@ private:
int m_states; int m_states;
int m_vocation; int m_vocation;
int m_blessings;
double m_health; double m_health;
double m_maxHealth; double m_maxHealth;

View File

@ -63,6 +63,7 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_things", "isDatLoaded", &ThingTypeManager::isDatLoaded, &g_things); g_lua.bindSingletonFunction("g_things", "isDatLoaded", &ThingTypeManager::isDatLoaded, &g_things);
g_lua.bindSingletonFunction("g_things", "isOtbLoaded", &ThingTypeManager::isOtbLoaded, &g_things); g_lua.bindSingletonFunction("g_things", "isOtbLoaded", &ThingTypeManager::isOtbLoaded, &g_things);
g_lua.bindSingletonFunction("g_things", "getDatSignature", &ThingTypeManager::getDatSignature, &g_things); g_lua.bindSingletonFunction("g_things", "getDatSignature", &ThingTypeManager::getDatSignature, &g_things);
g_lua.bindSingletonFunction("g_things", "getContentRevision", &ThingTypeManager::getContentRevision, &g_things);
g_lua.bindSingletonFunction("g_things", "getThingType", &ThingTypeManager::getThingType, &g_things); g_lua.bindSingletonFunction("g_things", "getThingType", &ThingTypeManager::getThingType, &g_things);
g_lua.bindSingletonFunction("g_things", "getItemType", &ThingTypeManager::getItemType, &g_things); g_lua.bindSingletonFunction("g_things", "getItemType", &ThingTypeManager::getItemType, &g_things);
g_lua.bindSingletonFunction("g_things", "getThingTypes", &ThingTypeManager::getThingTypes, &g_things); g_lua.bindSingletonFunction("g_things", "getThingTypes", &ThingTypeManager::getThingTypes, &g_things);
@ -630,6 +631,7 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<LocalPlayer>("getTotalCapacity", &LocalPlayer::getTotalCapacity); g_lua.bindClassMemberFunction<LocalPlayer>("getTotalCapacity", &LocalPlayer::getTotalCapacity);
g_lua.bindClassMemberFunction<LocalPlayer>("getInventoryItem", &LocalPlayer::getInventoryItem); g_lua.bindClassMemberFunction<LocalPlayer>("getInventoryItem", &LocalPlayer::getInventoryItem);
g_lua.bindClassMemberFunction<LocalPlayer>("getVocation", &LocalPlayer::getVocation); g_lua.bindClassMemberFunction<LocalPlayer>("getVocation", &LocalPlayer::getVocation);
g_lua.bindClassMemberFunction<LocalPlayer>("getBlessings", &LocalPlayer::getBlessings);
g_lua.bindClassMemberFunction<LocalPlayer>("isPremium", &LocalPlayer::isPremium); g_lua.bindClassMemberFunction<LocalPlayer>("isPremium", &LocalPlayer::isPremium);
g_lua.bindClassMemberFunction<LocalPlayer>("isKnown", &LocalPlayer::isKnown); g_lua.bindClassMemberFunction<LocalPlayer>("isKnown", &LocalPlayer::isKnown);
g_lua.bindClassMemberFunction<LocalPlayer>("isPreWalking", &LocalPlayer::isPreWalking); g_lua.bindClassMemberFunction<LocalPlayer>("isPreWalking", &LocalPlayer::isPreWalking);

View File

@ -28,7 +28,52 @@ std::map<uint8, uint8> messageModesMap;
void buildMessageModesMap(int version) { void buildMessageModesMap(int version) {
messageModesMap.clear(); messageModesMap.clear();
if(version >= 1036) {
if(version >= 1055) { // might be 1054
messageModesMap[Otc::MessageNone] = 0;
messageModesMap[Otc::MessageSay] = 1;
messageModesMap[Otc::MessageWhisper] = 2;
messageModesMap[Otc::MessageYell] = 3;
messageModesMap[Otc::MessagePrivateFrom] = 4;
messageModesMap[Otc::MessagePrivateTo] = 5;
messageModesMap[Otc::MessageChannelManagement] = 6;
messageModesMap[Otc::MessageChannel] = 7;
messageModesMap[Otc::MessageChannelHighlight] = 8;
messageModesMap[Otc::MessageSpell] = 9;
//NpcFromStartBlock = 10
messageModesMap[Otc::MessageNpcFrom] = 11;
messageModesMap[Otc::MessageNpcTo] = 12;
messageModesMap[Otc::MessageGamemasterBroadcast] = 13;
messageModesMap[Otc::MessageGamemasterChannel] = 14;
messageModesMap[Otc::MessageGamemasterPrivateFrom] = 15;
messageModesMap[Otc::MessageGamemasterPrivateTo] = 16;
messageModesMap[Otc::MessageLogin] = 17;
messageModesMap[Otc::MessageWarning] = 18; // Admin
messageModesMap[Otc::MessageGame] = 19;
messageModesMap[Otc::MessageGameHighlight] = 20;
messageModesMap[Otc::MessageFailure] = 21;
messageModesMap[Otc::MessageLook] = 22;
messageModesMap[Otc::MessageDamageDealed] = 23;
messageModesMap[Otc::MessageDamageReceived] = 24;
messageModesMap[Otc::MessageHeal] = 25;
messageModesMap[Otc::MessageExp] = 26;
messageModesMap[Otc::MessageDamageOthers] = 27;
messageModesMap[Otc::MessageHealOthers] = 28;
messageModesMap[Otc::MessageExpOthers] = 29;
messageModesMap[Otc::MessageStatus] = 30;
messageModesMap[Otc::MessageLoot] = 31;
messageModesMap[Otc::MessageTradeNpc] = 32;
messageModesMap[Otc::MessageGuild] = 33;
messageModesMap[Otc::MessagePartyManagement] = 34;
messageModesMap[Otc::MessageParty] = 35;
messageModesMap[Otc::MessageBarkLow] = 36;
messageModesMap[Otc::MessageBarkLoud] = 37;
messageModesMap[Otc::MessageReport] = 38;
messageModesMap[Otc::MessageHotkeyUse] = 39;
messageModesMap[Otc::MessageTutorialHint] = 40;
messageModesMap[Otc::MessageThankyou] = 41;
messageModesMap[Otc::MessageMarket] = 42;
} else if(version >= 1036) {
for(int i = Otc::MessageNone; i <= Otc::MessageBeyondLast; ++i) { for(int i = Otc::MessageNone; i <= Otc::MessageBeyondLast; ++i) {
if(i >= Otc::MessageNpcTo) if(i >= Otc::MessageNpcTo)
messageModesMap[i] = i + 1; messageModesMap[i] = i + 1;

View File

@ -107,6 +107,8 @@ namespace Proto {
GameServerCreatureType = 149, GameServerCreatureType = 149,
GameServerEditText = 150, GameServerEditText = 150,
GameServerEditList = 151, GameServerEditList = 151,
GameServerBlessings = 156,
GameServerPreset = 157,
GameServerPremiumTrigger = 158, // 1038 GameServerPremiumTrigger = 158, // 1038
GameServerPlayerDataBasic = 159, // 950 GameServerPlayerDataBasic = 159, // 950
GameServerPlayerData = 160, GameServerPlayerData = 160,
@ -130,6 +132,8 @@ namespace Proto {
GameServerTextMessage = 180, GameServerTextMessage = 180,
GameServerCancelWalk = 181, GameServerCancelWalk = 181,
GameServerWalkWait = 182, GameServerWalkWait = 182,
GameServerUnjustifiedStats = 183,
GameServerPvpSituations = 184,
GameServerFloorChangeUp = 190, GameServerFloorChangeUp = 190,
GameServerFloorChangeDown = 191, GameServerFloorChangeDown = 191,
GameServerChooseOutfit = 200, GameServerChooseOutfit = 200,

View File

@ -56,7 +56,7 @@ void ProtocolGame::onRecv(const InputMessagePtr& inputMessage)
if(m_firstRecv) { if(m_firstRecv) {
m_firstRecv = false; m_firstRecv = false;
if(g_game.getClientVersion() >= 841) { // not sure since which version this is, but it seems to be after 8.40 if(g_game.getFeature(Otc::GameMessageSizeCheck)) {
int size = inputMessage->getU16(); int size = inputMessage->getU16();
if(size != inputMessage->getUnreadSize()) { if(size != inputMessage->getUnreadSize()) {
g_logger.traceError("invalid message size"); g_logger.traceError("invalid message size");

View File

@ -128,6 +128,10 @@ public:
void addPosition(const OutputMessagePtr& msg, const Position& position); void addPosition(const OutputMessagePtr& msg, const Position& position);
private: private:
void parseBlessings(const InputMessagePtr& msg);
void parseUnjustifiedStats(const InputMessagePtr& msg);
void parsePvpSituations(const InputMessagePtr& msg);
void parsePreset(const InputMessagePtr& msg);
void parseCreatureType(const InputMessagePtr& msg); void parseCreatureType(const InputMessagePtr& msg);
void parsePlayerHelpers(const InputMessagePtr& msg); void parsePlayerHelpers(const InputMessagePtr& msg);
void parseMessage(const InputMessagePtr& msg); void parseMessage(const InputMessagePtr& msg);

View File

@ -340,6 +340,19 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
case Proto::GameServerCreatureType: case Proto::GameServerCreatureType:
parseCreatureType(msg); parseCreatureType(msg);
break; break;
// PROTOCOL>=1055
case Proto::GameServerBlessings:
parseBlessings(msg);
break;
case Proto::GameServerUnjustifiedStats:
parseUnjustifiedStats(msg);
break;
case Proto::GameServerPvpSituations:
parsePvpSituations(msg);
break;
case Proto::GameServerPreset:
parsePreset(msg);
break;
// otclient ONLY // otclient ONLY
case Proto::GameServerExtendedOpcode: case Proto::GameServerExtendedOpcode:
parseExtendedOpcode(msg); parseExtendedOpcode(msg);
@ -372,6 +385,9 @@ void ProtocolGame::parseLogin(const InputMessagePtr& msg)
} }
bool canReportBugs = msg->getU8(); bool canReportBugs = msg->getU8();
msg->getU8(); // can change pvp framing option
msg->getU8(); // expert mode enabled
m_localPlayer->setId(playerId); m_localPlayer->setId(playerId);
g_game.setServerBeat(serverBeat); g_game.setServerBeat(serverBeat);
g_game.setCanReportBugs(canReportBugs); g_game.setCanReportBugs(canReportBugs);
@ -396,6 +412,34 @@ void ProtocolGame::parseEnterGame(const InputMessagePtr& msg)
} }
} }
void ProtocolGame::parseBlessings(const InputMessagePtr& msg)
{
uint16 blessings = msg->getU16();
m_localPlayer->setBlessings(blessings);
}
void ProtocolGame::parsePreset(const InputMessagePtr& msg)
{
uint16 preset = msg->getU32();
}
void ProtocolGame::parseUnjustifiedStats(const InputMessagePtr& msg)
{
// Unjustified Kills display since 10.55
msg->getU8();
msg->getU8();
msg->getU8();
msg->getU8();
msg->getU8();
msg->getU8();
msg->getU8();
}
void ProtocolGame::parsePvpSituations(const InputMessagePtr& msg)
{
msg->getU8(); // amount of open pvp situations
}
void ProtocolGame::parsePlayerHelpers(const InputMessagePtr& msg) void ProtocolGame::parsePlayerHelpers(const InputMessagePtr& msg)
{ {
uint id = msg->getU32(); uint id = msg->getU32();
@ -468,6 +512,7 @@ void ProtocolGame::parseChallenge(const InputMessagePtr& msg)
{ {
uint timestamp = msg->getU32(); uint timestamp = msg->getU32();
uint8 random = msg->getU8(); uint8 random = msg->getU8();
sendLoginPacket(timestamp, random); sendLoginPacket(timestamp, random);
} }
@ -1038,7 +1083,7 @@ void ProtocolGame::parsePlayerInfo(const InputMessagePtr& msg)
bool premium = msg->getU8(); // premium bool premium = msg->getU8(); // premium
int vocation = msg->getU8(); // vocation int vocation = msg->getU8(); // vocation
if(g_game.getFeature(Otc::GamePremiumExpiration)) if(g_game.getFeature(Otc::GamePremiumExpiration))
int premiumEx = msg->getU32(); // premium expiration int premiumEx = msg->getU32(); // premium expiration used for premium advertisement
int spellCount = msg->getU16(); int spellCount = msg->getU16();
std::vector<int> spells; std::vector<int> spells;
@ -1081,6 +1126,10 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
double level = msg->getU16(); double level = msg->getU16();
double levelPercent = msg->getU8(); double levelPercent = msg->getU8();
if(g_game.getFeature(Otc::GameExperienceBonus))
double experienceBonus = msg->getDouble();
double mana; double mana;
double maxMana; double maxMana;
@ -1145,7 +1194,7 @@ void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg)
int baseLevel; int baseLevel;
if(g_game.getFeature(Otc::GameSkillsBase)) if(g_game.getFeature(Otc::GameSkillsBase))
if (g_game.getFeature(Otc::GameBaseSkillU16)) if(g_game.getFeature(Otc::GameBaseSkillU16))
baseLevel = msg->getU16(); baseLevel = msg->getU16();
else else
baseLevel = msg->getU8(); baseLevel = msg->getU8();
@ -1699,7 +1748,7 @@ void ProtocolGame::parseChangeMapAwareRange(const InputMessagePtr& msg)
void ProtocolGame::parseCreaturesMark(const InputMessagePtr& msg) void ProtocolGame::parseCreaturesMark(const InputMessagePtr& msg)
{ {
int len; int len;
if (g_game.getClientVersion() >= 1035) { if(g_game.getClientVersion() >= 1035) {
len = 1; len = 1;
} else { } else {
len = msg->getU8(); len = msg->getU8();

View File

@ -56,17 +56,20 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addU16(g_game.getOs()); msg->addU16(g_game.getOs());
msg->addU16(g_game.getProtocolVersion()); msg->addU16(g_game.getProtocolVersion());
if(g_game.getClientVersion() >= 980) { if(g_game.getFeature(Otc::GameClientVersion))
msg->addU32(g_game.getClientVersion()); msg->addU32(g_game.getClientVersion());
msg->addU8(0); // preview state
} if(g_game.getFeature(Otc::GameContentRevision))
msg->addU16(g_things.getContentRevision());
if(g_game.getFeature(Otc::GamePreviewState))
msg->addU8(0);
int offset = msg->getMessageSize(); int offset = msg->getMessageSize();
// first RSA byte must be 0
msg->addU8(0);
msg->addU8(0); // first RSA byte must be 0 if(g_game.getFeature(Otc::GameLoginPacketEncryption)) {
if(g_game.getClientVersion() >= 770)
{
// xtea key // xtea key
generateXteaKey(); generateXteaKey();
msg->addU32(m_xteaKey[0]); msg->addU32(m_xteaKey[0]);
@ -99,7 +102,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addPaddingBytes(paddingBytes); msg->addPaddingBytes(paddingBytes);
// encrypt with RSA // encrypt with RSA
if(g_game.getClientVersion() >= 770) if(g_game.getFeature(Otc::GameLoginPacketEncryption))
msg->encryptRsa(); msg->encryptRsa();
if(g_game.getFeature(Otc::GameProtocolChecksum)) if(g_game.getFeature(Otc::GameProtocolChecksum))
@ -107,7 +110,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
send(msg); send(msg);
if(g_game.getClientVersion() >= 770) if(g_game.getFeature(Otc::GameLoginPacketEncryption))
enableXteaEncryption(); enableXteaEncryption();
} }

View File

@ -274,51 +274,60 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS
stdext::throw_exception(stdext::format("corrupt data (id: %d, category: %d, count: %d, lastAttr: %d)", stdext::throw_exception(stdext::format("corrupt data (id: %d, category: %d, count: %d, lastAttr: %d)",
m_id, m_category, count, attr)); m_id, m_category, count, attr));
uint8 width = fin->getU8(); uint8 frames = 1;
uint8 height = fin->getU8(); if(category == ThingCategoryCreature && g_game.getClientVersion() >= 1057)
m_size = Size(width, height); frames = fin->getU8();
if(width > 1 || height > 1) {
m_realSize = fin->getU8();
m_exactSize = std::min<int>(m_realSize, std::max<int>(width * 32, height * 32));
}
else
m_exactSize = 32;
m_layers = fin->getU8(); for(int i = 0; i < frames; ++i) {
m_numPatternX = fin->getU8(); uint8 frameGroup = FrameGroupDefault;
m_numPatternY = fin->getU8(); if(category == ThingCategoryCreature && g_game.getClientVersion() >= 1057) {
if(g_game.getClientVersion() >= 755) frameGroup = fin->getU8();
m_numPatternZ = fin->getU8(); }
else
m_numPatternZ = 1;
m_animationPhases = fin->getU8();
if(g_game.getFeature(Otc::GameEnhancedAnimations)) { uint8 width = fin->getU8();
if(m_animationPhases > 1) { uint8 height = fin->getU8();
m_animation.async = fin->getU8() == 0; m_size = Size(width, height);
m_animation.loopCount = fin->get32(); if(width > 1 || height > 1) {
m_animation.startIndex = fin->getU8(); m_realSize = fin->getU8();
m_exactSize = std::min<int>(m_realSize, std::max<int>(width * 32, height * 32));
}
else
m_exactSize = 32;
for(int i = 0; i < m_animationPhases; i++) { m_layers = fin->getU8();
int minDuration = fin->getU32(); m_numPatternX = fin->getU8();
int maxDuration = fin->getU32(); m_numPatternY = fin->getU8();
if(g_game.getClientVersion() >= 755)
m_numPatternZ = fin->getU8();
else
m_numPatternZ = 1;
m_animationPhases = fin->getU8();
m_animation.frames.push_back(std::make_tuple(minDuration, maxDuration)); if(g_game.getFeature(Otc::GameEnhancedAnimations)) {
if(m_animationPhases > 1) {
m_animation.async = fin->getU8() == 0;
m_animation.loopCount = fin->get32();
m_animation.startIndex = fin->getU8();
for (int i = 0; i < m_animationPhases; i++) {
int minDuration = fin->getU32();
int maxDuration = fin->getU32();
m_animation.frames.push_back(std::make_tuple(minDuration, maxDuration));
}
} }
} }
int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases;
if(totalSprites > 4096)
stdext::throw_exception("a thing type has more than 4096 sprites");
m_spritesIndex.resize(totalSprites);
for(int i = 0; i < totalSprites; i++)
m_spritesIndex[i] = g_game.getFeature(Otc::GameSpritesU32) ? fin->getU32() : fin->getU16();
} }
int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases;
// if(totalSprites == 0)
// stdext::throw_exception("a thing type has no sprites");
if(totalSprites > 4096)
stdext::throw_exception("a thing type has more than 4096 sprites");
m_spritesIndex.resize(totalSprites);
for(int i = 0; i < totalSprites; i++)
m_spritesIndex[i] = g_game.getFeature(Otc::GameSpritesU32) ? fin->getU32() : fin->getU16();
m_textures.resize(m_animationPhases); m_textures.resize(m_animationPhases);
m_texturesFramesRects.resize(m_animationPhases); m_texturesFramesRects.resize(m_animationPhases);
m_texturesFramesOriginRects.resize(m_animationPhases); m_texturesFramesOriginRects.resize(m_animationPhases);

View File

@ -32,6 +32,12 @@
#include <framework/luaengine/luaobject.h> #include <framework/luaengine/luaobject.h>
#include <framework/net/server.h> #include <framework/net/server.h>
enum FrameGroup : uint8 {
FrameGroupIdle = 0,
FrameGroupMoving,
FrameGroupDefault = FrameGroupIdle
};
enum ThingCategory : uint8 { enum ThingCategory : uint8 {
ThingCategoryItem = 0, ThingCategoryItem = 0,
ThingCategoryCreature, ThingCategoryCreature,

View File

@ -42,6 +42,7 @@ void ThingTypeManager::init()
m_nullThingType = ThingTypePtr(new ThingType); m_nullThingType = ThingTypePtr(new ThingType);
m_nullItemType = ItemTypePtr(new ItemType); m_nullItemType = ItemTypePtr(new ItemType);
m_datSignature = 0; m_datSignature = 0;
m_contentRevision = 0;
m_otbMinorVersion = 0; m_otbMinorVersion = 0;
m_otbMajorVersion = 0; m_otbMajorVersion = 0;
m_datLoaded = false; m_datLoaded = false;
@ -100,12 +101,14 @@ bool ThingTypeManager::loadDat(std::string file)
{ {
m_datLoaded = false; m_datLoaded = false;
m_datSignature = 0; m_datSignature = 0;
m_contentRevision = 0;
try { try {
file = g_resources.guessFilePath(file, "dat"); file = g_resources.guessFilePath(file, "dat");
FileStreamPtr fin = g_resources.openFile(file); FileStreamPtr fin = g_resources.openFile(file);
m_datSignature = fin->getU32(); m_datSignature = fin->getU32();
m_contentRevision = static_cast<uint16_t>(m_datSignature);
for(int category = 0; category < ThingLastCategory; ++category) { for(int category = 0; category < ThingLastCategory; ++category) {
int count = fin->getU16() + 1; int count = fin->getU16() + 1;

View File

@ -66,6 +66,7 @@ public:
uint32 getDatSignature() { return m_datSignature; } uint32 getDatSignature() { return m_datSignature; }
uint32 getOtbMajorVersion() { return m_otbMajorVersion; } uint32 getOtbMajorVersion() { return m_otbMajorVersion; }
uint32 getOtbMinorVersion() { return m_otbMinorVersion; } uint32 getOtbMinorVersion() { return m_otbMinorVersion; }
uint16 getContentRevision() { return m_contentRevision; }
bool isDatLoaded() { return m_datLoaded; } bool isDatLoaded() { return m_datLoaded; }
bool isXmlLoaded() { return m_xmlLoaded; } bool isXmlLoaded() { return m_xmlLoaded; }
@ -89,6 +90,7 @@ private:
uint32 m_otbMinorVersion; uint32 m_otbMinorVersion;
uint32 m_otbMajorVersion; uint32 m_otbMajorVersion;
uint32 m_datSignature; uint32 m_datSignature;
uint16 m_contentRevision;
}; };
extern ThingTypeManager g_things; extern ThingTypeManager g_things;