• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Free task module made by @zQt

Gubailovo

Well-Known Member
Joined
Dec 19, 2013
Messages
407
Solutions
2
Reaction score
62
I am trying to install this system downloaded from Open-Tibia Academy
but after installation, the client gives an error
C++:
ERROR: Unable to send extended opcode 215, extended opcodes are not enabled on this server.
my actions during installation
D:\forgottenserver-1.4.2\data\creaturescripts\scripts\login.lua
Lua:
function onLogin(player)
    local serverName = configManager.getString(configKeys.SERVER_NAME)
    local loginStr = "Welcome to " .. serverName .. "!"
    if player:getLastLoginSaved() <= 0 then
        loginStr = loginStr .. " Please choose your outfit."
        player:sendOutfitWindow()
    else
        if loginStr ~= "" then
            player:sendTextMessage(MESSAGE_STATUS_DEFAULT, loginStr)
        end

        loginStr = string.format("Your last visit in %s: %s.", serverName, os.date("%d %b %Y %X", player:getLastLoginSaved()))
    end
    player:sendTextMessage(MESSAGE_STATUS_DEFAULT, loginStr)

    -- Promotion
    local vocation = player:getVocation()
    local promotion = vocation:getPromotion()
    if player:isPremium() then
        local value = player:getStorageValue(PlayerStorageKeys.promotion)
        if value == 1 then
            player:setVocation(promotion)
        end
    elseif not promotion then
        player:setVocation(vocation:getDemotion())
    end

    -- Events
    player:registerEvent("PlayerDeath")
    player:registerEvent("DropLoot")
    player:registerEvent("Dialogue")
    player:registerEvent("TaskKill")
    player:registerEvent("ExtendedOpcode")
    
    return true
end
D:\forgottenserver-1.4.2\data\lib\core\json.lua
Lua:
--
-- json.lua
--
-- Copyright (c) 2019 rxi
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
--

local json = { _version = "0.1.2" }

-------------------------------------------------------------------------------
-- Encode
-------------------------------------------------------------------------------

local encode

local escape_char_map = {
  [ "\\" ] = "\\\\",
  [ "\"" ] = "\\\"",
  [ "\b" ] = "\\b",
  [ "\f" ] = "\\f",
  [ "\n" ] = "\\n",
  [ "\r" ] = "\\r",
  [ "\t" ] = "\\t",
}

local escape_char_map_inv = { [ "\\/" ] = "/" }
for k, v in pairs(escape_char_map) do
  escape_char_map_inv[v] = k
end


local function escape_char(c)
  return escape_char_map[c] or string.format("\\u%04x", c:byte())
end


local function encode_nil(val)
  return "null"
end


local function encode_table(val, stack)
  local res = {}
  stack = stack or {}

  -- Circular reference?
  if stack[val] then error("circular reference") end

  stack[val] = true

  if rawget(val, 1) ~= nil or next(val) == nil then
    -- Treat as array -- check keys are valid and it is not sparse
    local n = 0
    for k in pairs(val) do
      if type(k) ~= "number" then
        error("invalid table: mixed or invalid key types")
      end
      n = n + 1
    end
    if n ~= #val then
      error("invalid table: sparse array")
    end
    -- Encode
    for i, v in ipairs(val) do
      table.insert(res, encode(v, stack))
    end
    stack[val] = nil
    return "[" .. table.concat(res, ",") .. "]"

  else
    -- Treat as an object
    for k, v in pairs(val) do
      if type(k) ~= "string" then
        error("invalid table: mixed or invalid key types")
      end
      table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
    end
    stack[val] = nil
    return "{" .. table.concat(res, ",") .. "}"
  end
end


local function encode_string(val)
  return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
end


local function encode_number(val)
  -- Check for NaN, -inf and inf
  if val ~= val or val <= -math.huge or val >= math.huge then
    error("unexpected number value '" .. tostring(val) .. "'")
  end
  return string.format("%.14g", val)
end


local type_func_map = {
  [ "nil"     ] = encode_nil,
  [ "table"   ] = encode_table,
  [ "string"  ] = encode_string,
  [ "number"  ] = encode_number,
  [ "boolean" ] = tostring,
}


encode = function(val, stack)
  local t = type(val)
  local f = type_func_map[t]
  if f then
    return f(val, stack)
  end
  error("unexpected type '" .. t .. "'")
end


function json.encode(val)
  return ( encode(val) )
end


-------------------------------------------------------------------------------
-- Decode
-------------------------------------------------------------------------------

local parse

local function create_set(...)
  local res = {}
  for i = 1, select("#", ...) do
    res[ select(i, ...) ] = true
  end
  return res
end

local space_chars   = create_set(" ", "\t", "\r", "\n")
local delim_chars   = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
local escape_chars  = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
local literals      = create_set("true", "false", "null")

local literal_map = {
  [ "true"  ] = true,
  [ "false" ] = false,
  [ "null"  ] = nil,
}


local function next_char(str, idx, set, negate)
  for i = idx, #str do
    if set[str:sub(i, i)] ~= negate then
      return i
    end
  end
  return #str + 1
end


local function decode_error(str, idx, msg)
  local line_count = 1
  local col_count = 1
  for i = 1, idx - 1 do
    col_count = col_count + 1
    if str:sub(i, i) == "\n" then
      line_count = line_count + 1
      col_count = 1
    end
  end
  error( string.format("%s at line %d col %d", msg, line_count, col_count) )
end


local function codepoint_to_utf8(n)
  -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
  local f = math.floor
  if n <= 0x7f then
    return string.char(n)
  elseif n <= 0x7ff then
    return string.char(f(n / 64) + 192, n % 64 + 128)
  elseif n <= 0xffff then
    return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
  elseif n <= 0x10ffff then
    return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
                       f(n % 4096 / 64) + 128, n % 64 + 128)
  end
  error( string.format("invalid unicode codepoint '%x'", n) )
end


local function parse_unicode_escape(s)
  local n1 = tonumber( s:sub(3, 6),  16 )
  local n2 = tonumber( s:sub(9, 12), 16 )
  -- Surrogate pair?
  if n2 then
    return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
  else
    return codepoint_to_utf8(n1)
  end
end


local function parse_string(str, i)
  local has_unicode_escape = false
  local has_surrogate_escape = false
  local has_escape = false
  local last
  for j = i + 1, #str do
    local x = str:byte(j)

    if x < 32 then
      decode_error(str, j, "control character in string")
    end

    if last == 92 then -- "\\" (escape char)
      if x == 117 then -- "u" (unicode escape sequence)
        local hex = str:sub(j + 1, j + 5)
        if not hex:find("%x%x%x%x") then
          decode_error(str, j, "invalid unicode escape in string")
        end
        if hex:find("^[dD][89aAbB]") then
          has_surrogate_escape = true
        else
          has_unicode_escape = true
        end
      else
        local c = string.char(x)
        if not escape_chars[c] then
          decode_error(str, j, "invalid escape char '" .. c .. "' in string")
        end
        has_escape = true
      end
      last = nil

    elseif x == 34 then -- '"' (end of string)
      local s = str:sub(i + 1, j - 1)
      if has_surrogate_escape then
        s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
      end
      if has_unicode_escape then
        s = s:gsub("\\u....", parse_unicode_escape)
      end
      if has_escape then
        s = s:gsub("\\.", escape_char_map_inv)
      end
      return s, j + 1

    else
      last = x
    end
  end
  decode_error(str, i, "expected closing quote for string")
end


local function parse_number(str, i)
  local x = next_char(str, i, delim_chars)
  local s = str:sub(i, x - 1)
  local n = tonumber(s)
  if not n then
    decode_error(str, i, "invalid number '" .. s .. "'")
  end
  return n, x
end


local function parse_literal(str, i)
  local x = next_char(str, i, delim_chars)
  local word = str:sub(i, x - 1)
  if not literals[word] then
    decode_error(str, i, "invalid literal '" .. word .. "'")
  end
  return literal_map[word], x
end


local function parse_array(str, i)
  local res = {}
  local n = 1
  i = i + 1
  while 1 do
    local x
    i = next_char(str, i, space_chars, true)
    -- Empty / end of array?
    if str:sub(i, i) == "]" then
      i = i + 1
      break
    end
    -- Read token
    x, i = parse(str, i)
    res[n] = x
    n = n + 1
    -- Next token
    i = next_char(str, i, space_chars, true)
    local chr = str:sub(i, i)
    i = i + 1
    if chr == "]" then break end
    if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
  end
  return res, i
end


local function parse_object(str, i)
  local res = {}
  i = i + 1
  while 1 do
    local key, val
    i = next_char(str, i, space_chars, true)
    -- Empty / end of object?
    if str:sub(i, i) == "}" then
      i = i + 1
      break
    end
    -- Read key
    if str:sub(i, i) ~= '"' then
      decode_error(str, i, "expected string for key")
    end
    key, i = parse(str, i)
    -- Read ':' delimiter
    i = next_char(str, i, space_chars, true)
    if str:sub(i, i) ~= ":" then
      decode_error(str, i, "expected ':' after key")
    end
    i = next_char(str, i + 1, space_chars, true)
    -- Read value
    val, i = parse(str, i)
    -- Set
    res[key] = val
    -- Next token
    i = next_char(str, i, space_chars, true)
    local chr = str:sub(i, i)
    i = i + 1
    if chr == "}" then break end
    if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
  end
  return res, i
end


local char_func_map = {
  [ '"' ] = parse_string,
  [ "0" ] = parse_number,
  [ "1" ] = parse_number,
  [ "2" ] = parse_number,
  [ "3" ] = parse_number,
  [ "4" ] = parse_number,
  [ "5" ] = parse_number,
  [ "6" ] = parse_number,
  [ "7" ] = parse_number,
  [ "8" ] = parse_number,
  [ "9" ] = parse_number,
  [ "-" ] = parse_number,
  [ "t" ] = parse_literal,
  [ "f" ] = parse_literal,
  [ "n" ] = parse_literal,
  [ "[" ] = parse_array,
  [ "{" ] = parse_object,
}


parse = function(str, idx)
  local chr = str:sub(idx, idx)
  local f = char_func_map[chr]
  if f then
    return f(str, idx)
  end
  decode_error(str, idx, "unexpected character '" .. chr .. "'")
end


function json.decode(str)
  if type(str) ~= "string" then
    error("expected argument of type string, got " .. type(str))
  end
  local res, idx = parse(str, next_char(str, 1, space_chars, true))
  idx = next_char(str, idx, space_chars, true)
  if idx <= #str then
    decode_error(str, idx, "trailing garbage")
  end
  return res
end


return json
D:\forgottenserver-1.4.2\data\lib\core\player.lua
Lua:
local foodCondition = Condition(CONDITION_REGENERATION, CONDITIONID_DEFAULT)

function Player.feed(self, food)
    local condition = self:getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT)
    if condition then
        condition:setTicks(condition:getTicks() + (food * 1000))
    else
        local vocation = self:getVocation()
        if not vocation then
            return nil
        end

        foodCondition:setTicks(food * 1000)
        foodCondition:setParameter(CONDITION_PARAM_HEALTHGAIN, vocation:getHealthGainAmount())
        foodCondition:setParameter(CONDITION_PARAM_HEALTHTICKS, vocation:getHealthGainTicks() * 1000)
        foodCondition:setParameter(CONDITION_PARAM_MANAGAIN, vocation:getManaGainAmount())
        foodCondition:setParameter(CONDITION_PARAM_MANATICKS, vocation:getManaGainTicks() * 1000)

        self:addCondition(foodCondition)
    end
    return true
end

function Player.getClosestFreePosition(self, position, extended)
    if self:getGroup():getAccess() and self:getAccountType() >= ACCOUNT_TYPE_GOD then
        return position
    end
    return Creature.getClosestFreePosition(self, position, extended)
end

function Player.getDepotItems(self, depotId)
    return self:getDepotChest(depotId, true):getItemHoldingCount()
end

function Player.hasFlag(self, flag)
    return self:getGroup():hasFlag(flag)
end

function Player.getLossPercent(self)
    local blessings = 0
    local lossPercent = {
        [0] = 100,
        [1] = 70,
        [2] = 45,
        [3] = 25,
        [4] = 10,
        [5] = 0
    }

    for i = 1, 5 do
        if self:hasBlessing(i) then
            blessings = blessings + 1
        end
    end
    return lossPercent[blessings]
end
function Player.sendExtendedJSONOpcode(self, opcode, buffer)
    if not self:isUsingOtClient() then
        return false
    end

    local networkMessage = NetworkMessage()
    networkMessage:addByte(0x32)
    networkMessage:addByte(opcode)
    networkMessage:addString(json.encode(buffer))
    networkMessage:sendToPlayer(self)
    networkMessage:delete()
    return true
end
function Player.getPremiumTime(self)
    return math.max(0, self:getPremiumEndsAt() - os.time())
end

function Player.setPremiumTime(self, seconds)
    self:setPremiumEndsAt(os.time() + seconds)
    return true
end

function Player.addPremiumTime(self, seconds)
    self:setPremiumTime(self:getPremiumTime() + seconds)
    return true
end

function Player.removePremiumTime(self, seconds)
    local currentTime = self:getPremiumTime()
    if currentTime < seconds then
        return false
    end

    self:setPremiumTime(currentTime - seconds)
    return true
end

function Player.getPremiumDays(self)
    return math.floor(self:getPremiumTime() / 86400)
end

function Player.addPremiumDays(self, days)
    return self:addPremiumTime(days * 86400)
end

function Player.removePremiumDays(self, days)
    return self:removePremiumTime(days * 86400)
end

function Player.isPremium(self)
    return self:getPremiumTime() > 0 or configManager.getBoolean(configKeys.FREE_PREMIUM) or self:hasFlag(PlayerFlag_IsAlwaysPremium)
end

function Player.sendCancelMessage(self, message)
    if type(message) == "number" then
        message = Game.getReturnMessage(message)
    end
    return self:sendTextMessage(MESSAGE_STATUS_SMALL, message)
end

function Player.isUsingOtClient(self)
    return self:getClient().os >= CLIENTOS_OTCLIENT_LINUX
end

function Player.sendExtendedOpcode(self, opcode, buffer)
    if not self:isUsingOtClient() then
        return false
    end

    local networkMessage = NetworkMessage()
    networkMessage:addByte(0x32)
    networkMessage:addByte(opcode)
    networkMessage:addString(buffer)
    networkMessage:sendToPlayer(self)
    networkMessage:delete()
    return true
end

APPLY_SKILL_MULTIPLIER = true
local addSkillTriesFunc = Player.addSkillTries
function Player.addSkillTries(...)
    APPLY_SKILL_MULTIPLIER = false
    local ret = addSkillTriesFunc(...)
    APPLY_SKILL_MULTIPLIER = true
    return ret
end

local addManaSpentFunc = Player.addManaSpent
function Player.addManaSpent(...)
    APPLY_SKILL_MULTIPLIER = false
    local ret = addManaSpentFunc(...)
    APPLY_SKILL_MULTIPLIER = true
    return ret
end

-- Always pass the number through the isValidMoney function first before using the transferMoneyTo
function Player.transferMoneyTo(self, target, amount)
    if not target then
        return false
    end

    -- See if you can afford this transfer
    local balance = self:getBankBalance()
    if amount > balance then
        return false
    end

    -- See if player is online
    local targetPlayer = Player(target.guid)
    if targetPlayer then
        targetPlayer:setBankBalance(targetPlayer:getBankBalance() + amount)
    else
        db.query("UPDATE `players` SET `balance` = `balance` + " .. amount .. " WHERE `id` = '" .. target.guid .. "'")
    end

    self:setBankBalance(self:getBankBalance() - amount)
    return true
end

function Player.canCarryMoney(self, amount)
    -- Anyone can carry as much imaginary money as they desire
    if amount == 0 then
        return true
    end

    -- The 3 below loops will populate these local variables
    local totalWeight = 0
    local inventorySlots = 0

    -- Add crystal coins to totalWeight and inventorySlots
    local type_crystal = ItemType(ITEM_CRYSTAL_COIN)
    local crystalCoins = math.floor(amount / 10000)
    if crystalCoins > 0 then
        amount = amount - (crystalCoins * 10000)
        while crystalCoins > 0 do
            local count = math.min(100, crystalCoins)
            totalWeight = totalWeight + type_crystal:getWeight(count)
            crystalCoins = crystalCoins - count
            inventorySlots = inventorySlots + 1
        end
    end

    -- Add platinum coins to totalWeight and inventorySlots
    local type_platinum = ItemType(ITEM_PLATINUM_COIN)
    local platinumCoins = math.floor(amount / 100)
    if platinumCoins > 0 then
        amount = amount - (platinumCoins * 100)
        while platinumCoins > 0 do
            local count = math.min(100, platinumCoins)
            totalWeight = totalWeight + type_platinum:getWeight(count)
            platinumCoins = platinumCoins - count
            inventorySlots = inventorySlots + 1
        end
    end

    -- Add gold coins to totalWeight and inventorySlots
    local type_gold = ItemType(ITEM_GOLD_COIN)
    if amount > 0 then
        while amount > 0 do
            local count = math.min(100, amount)
            totalWeight = totalWeight + type_gold:getWeight(count)
            amount = amount - count
            inventorySlots = inventorySlots + 1
        end
    end

    -- If player don't have enough capacity to carry this money
    if self:getFreeCapacity() < totalWeight then
        return false
    end

    -- If player don't have enough available inventory slots to carry this money
    local backpack = self:getSlotItem(CONST_SLOT_BACKPACK)
    if not backpack or backpack:getEmptySlots(true) < inventorySlots then
        return false
    end
    return true
end

function Player.withdrawMoney(self, amount)
    local balance = self:getBankBalance()
    if amount > balance or not self:addMoney(amount) then
        return false
    end

    self:setBankBalance(balance - amount)
    return true
end

function Player.depositMoney(self, amount)
    if not self:removeMoney(amount) then
        return false
    end

    self:setBankBalance(self:getBankBalance() + amount)
    return true
end

function Player.removeTotalMoney(self, amount)
    local moneyCount = self:getMoney()
    local bankCount = self:getBankBalance()
    if amount <= moneyCount then
        self:removeMoney(amount)
        return true
    elseif amount <= (moneyCount + bankCount) then
        if moneyCount ~= 0 then
            self:removeMoney(moneyCount)
            local remains = amount - moneyCount
            self:setBankBalance(bankCount - remains)
            self:sendTextMessage(MESSAGE_INFO_DESCR, ("Paid %d from inventory and %d gold from bank account. Your account balance is now %d gold."):format(moneyCount, amount - moneyCount, self:getBankBalance()))
            return true
        else
            self:setBankBalance(bankCount - amount)
            self:sendTextMessage(MESSAGE_INFO_DESCR, ("Paid %d gold from bank account. Your account balance is now %d gold."):format(amount, self:getBankBalance()))
            return true
        end
    end
    return false
end

function Player.addLevel(self, amount, round)
    round = round or false
    local level, amount = self:getLevel(), amount or 1
    if amount > 0 then
        return self:addExperience(Game.getExperienceForLevel(level + amount) - (round and self:getExperience() or Game.getExperienceForLevel(level)))
    else
        return self:removeExperience(((round and self:getExperience() or Game.getExperienceForLevel(level)) - Game.getExperienceForLevel(level + amount)))
    end
end

function Player.addMagicLevel(self, value)
    local currentMagLevel = self:getBaseMagicLevel()
    local sum = 0

    if value > 0 then
        while value > 0 do
            sum = sum + self:getVocation():getRequiredManaSpent(currentMagLevel + value)
            value = value - 1
        end

        return self:addManaSpent(sum - self:getManaSpent())
    else
        value = math.min(currentMagLevel, math.abs(value))
        while value > 0 do
            sum = sum + self:getVocation():getRequiredManaSpent(currentMagLevel - value + 1)
            value = value - 1
        end

        return self:removeManaSpent(sum + self:getManaSpent())
    end
end

function Player.addSkillLevel(self, skillId, value)
    local currentSkillLevel = self:getSkillLevel(skillId)
    local sum = 0

    if value > 0 then
        while value > 0 do
            sum = sum + self:getVocation():getRequiredSkillTries(skillId, currentSkillLevel + value)
            value = value - 1
        end

        return self:addSkillTries(skillId, sum - self:getSkillTries(skillId))
    else
        value = math.min(currentSkillLevel, math.abs(value))
        while value > 0 do
            sum = sum + self:getVocation():getRequiredSkillTries(skillId, currentSkillLevel - value + 1)
            value = value - 1
        end

        return self:removeSkillTries(skillId, sum + self:getSkillTries(skillId), true)
    end
end

function Player.addSkill(self, skillId, value, round)
    if skillId == SKILL_LEVEL then
        return self:addLevel(value, round or false)
    elseif skillId == SKILL_MAGLEVEL then
        return self:addMagicLevel(value)
    end
    return self:addSkillLevel(skillId, value)
end

function Player.getWeaponType(self)
    local weapon = self:getSlotItem(CONST_SLOT_LEFT)
    if weapon then
        return weapon:getType():getWeaponType()
    end
    return WEAPON_NONE
end

function Player.getTotalMoney(self)
    return self:getMoney() + self:getBankBalance()
end
D:\forgottenserver-1.4.2\data\creaturescripts\scripts\extendedopcode.lua
Lua:
local OPCODE_LANGUAGE = 1

function onExtendedOpcode(player, opcode, buffer)
    if opcode == OPCODE_LANGUAGE then
        -- otclient language
        if buffer == 'en' or buffer == 'pt' then
            -- example, setting player language, because otclient is multi-language...
            -- player:setStorageValue(SOME_STORAGE_ID, SOME_VALUE)
        end
    elseif opcode == 215 then
        TaskSystem.onAction(player, json.decode(buffer))   
    else
        -- other opcodes can be ignored, and the server will just work fine...
    end
end
D:\forgottenserver-1.4.2\data\scripts\taskSystem.lua
Lua:
added
what is the problem?
I am using TFS 1.4.2 and OTCv8
/
otcv8-devq6xTZHS.png
Post automatically merged:

protocolgame.cpp
Скриншот 06-03-2023 201548.jpgСкриншот 06-03-2023 201438.jpg
Post automatically merged:

sorry I don't have any extended opcodes
 

Attachments

Last edited:
Solution
there are no errors on the server side.
and where to look? (tell me the way to g_game.enableFeature(GameExtendedOpcode) )
Post automatically merged:

turned off
Post automatically merged:

the client has now turned on without errors. and the server gives an error
C++:
Lua Script Error: [CreatureScript Interface]
data/creaturescripts/scripts/extendedopcode.lua:onExtendedOpcode
data/creaturescripts/scripts/extendedopcode.lua:11: attempt to index global 'json' (a nil value)
stack traceback:
        [C]: in function '__index'
        data/creaturescripts/scripts/extendedopcode.lua:11: in function <data/creaturescripts/scripts/extendedopcode.lua:3>
\data\lib\core\json.lua
try
@dewral
Lua:
--
-- json.lua
--
-- Copyright (c) 2018 rxi
--
--...
you have it registered in data/creaturescripts/creaturescripts.xml ?
<event type="extendedopcode" name="ExtendedOpcode" script="extendedopcode.lua" />
 
no errors on server side?

you have it enabled in otc?
g_game.enableFeature(GameExtendedOpcode)
there are no errors on the server side.
and where to look? (tell me the way to g_game.enableFeature(GameExtendedOpcode) )
Post automatically merged:

turned off
Post automatically merged:

the client has now turned on without errors. and the server gives an error
C++:
Lua Script Error: [CreatureScript Interface]
data/creaturescripts/scripts/extendedopcode.lua:onExtendedOpcode
data/creaturescripts/scripts/extendedopcode.lua:11: attempt to index global 'json' (a nil value)
stack traceback:
        [C]: in function '__index'
        data/creaturescripts/scripts/extendedopcode.lua:11: in function <data/creaturescripts/scripts/extendedopcode.lua:3>
 
Last edited:
there are no errors on the server side.
and where to look? (tell me the way to g_game.enableFeature(GameExtendedOpcode) )
Post automatically merged:

turned off
Post automatically merged:

the client has now turned on without errors. and the server gives an error
C++:
Lua Script Error: [CreatureScript Interface]
data/creaturescripts/scripts/extendedopcode.lua:onExtendedOpcode
data/creaturescripts/scripts/extendedopcode.lua:11: attempt to index global 'json' (a nil value)
stack traceback:
        [C]: in function '__index'
        data/creaturescripts/scripts/extendedopcode.lua:11: in function <data/creaturescripts/scripts/extendedopcode.lua:3>
you registered json.lua inside core.lua?
something like: dofile('data/lib/core/json.lua')
 
Server needs to send 0 with opcode 0x32 to enable this feature in OTClient:
Lua:
GameServerExtendedOpcode = 50



Operating system code is used for that purpouse, put breakpoint in VS here:

void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
C++:
OperatingSystem_t operatingSystem = static_cast<OperatingSystem_t>(msg.get<uint16_t>());
 
there are no errors on the server side.
and where to look? (tell me the way to g_game.enableFeature(GameExtendedOpcode) )
Post automatically merged:

turned off
Post automatically merged:

the client has now turned on without errors. and the server gives an error
C++:
Lua Script Error: [CreatureScript Interface]
data/creaturescripts/scripts/extendedopcode.lua:onExtendedOpcode
data/creaturescripts/scripts/extendedopcode.lua:11: attempt to index global 'json' (a nil value)
stack traceback:
        [C]: in function '__index'
        data/creaturescripts/scripts/extendedopcode.lua:11: in function <data/creaturescripts/scripts/extendedopcode.lua:3>
\data\lib\core\json.lua
try
@dewral
Lua:
--
-- json.lua
--
-- Copyright (c) 2018 rxi
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
--

json = { _version = "0.1.1" }

-------------------------------------------------------------------------------
-- Encode
-------------------------------------------------------------------------------

local encode

local escape_char_map = {
  [ "\\" ] = "\\\\",
  [ "\"" ] = "\\\"",
  [ "\b" ] = "\\b",
  [ "\f" ] = "\\f",
  [ "\n" ] = "\\n",
  [ "\r" ] = "\\r",
  [ "\t" ] = "\\t",
}

local escape_char_map_inv = { [ "\\/" ] = "/" }
for k, v in pairs(escape_char_map) do
  escape_char_map_inv[v] = k
end


local function escape_char(c)
  return escape_char_map[c] or string.format("\\u%04x", c:byte())
end


local function encode_nil(val)
  return "null"
end


local function encode_table(val, stack)
  local res = {}
  stack = stack or {}

  -- Circular reference?
  if stack[val] then error("circular reference") end

  stack[val] = true

  if val[1] ~= nil or next(val) == nil then
    -- Treat as array -- check keys are valid and it is not sparse
    local n = 0
    for k in pairs(val) do
      if type(k) ~= "number" then
        error("invalid table: mixed or invalid key types")
      end
      n = n + 1
    end
    if n ~= #val then
      error("invalid table: sparse array")
    end
    -- Encode
    for i, v in ipairs(val) do
      table.insert(res, encode(v, stack))
    end
    stack[val] = nil
    return "[" .. table.concat(res, ",") .. "]"

  else
    -- Treat as an object
    for k, v in pairs(val) do
      if type(k) ~= "string" then
        error("invalid table: mixed or invalid key types")
      end
      table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
    end
    stack[val] = nil
    return "{" .. table.concat(res, ",") .. "}"
  end
end


local function encode_string(val)
  return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
end


local function encode_number(val)
  -- Check for NaN, -inf and inf
  if val ~= val or val <= -math.huge or val >= math.huge then
    error("unexpected number value '" .. tostring(val) .. "'")
  end
  return string.format("%.14g", val)
end


local type_func_map = {
  [ "nil"     ] = encode_nil,
  [ "table"   ] = encode_table,
  [ "string"  ] = encode_string,
  [ "number"  ] = encode_number,
  [ "boolean" ] = tostring,
}


encode = function(val, stack)
  local t = type(val)
  local f = type_func_map[t]
  if f then
    return f(val, stack)
  end
  error("unexpected type '" .. t .. "'")
end


function json.encode(val)
  return ( encode(val) )
end


-------------------------------------------------------------------------------
-- Decode
-------------------------------------------------------------------------------

local parse

local function create_set(...)
  local res = {}
  for i = 1, select("#", ...) do
    res[ select(i, ...) ] = true
  end
  return res
end

local space_chars   = create_set(" ", "\t", "\r", "\n")
local delim_chars   = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
local escape_chars  = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
local literals      = create_set("true", "false", "null")

local literal_map = {
  [ "true"  ] = true,
  [ "false" ] = false,
  [ "null"  ] = nil,
}


local function next_char(str, idx, set, negate)
  for i = idx, #str do
    if set[str:sub(i, i)] ~= negate then
      return i
    end
  end
  return #str + 1
end


local function decode_error(str, idx, msg)
  local line_count = 1
  local col_count = 1
  for i = 1, idx - 1 do
    col_count = col_count + 1
    if str:sub(i, i) == "\n" then
      line_count = line_count + 1
      col_count = 1
    end
  end
  error( string.format("%s at line %d col %d", msg, line_count, col_count) )
end


local function codepoint_to_utf8(n)
  -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
  local f = math.floor
  if n <= 0x7f then
    return string.char(n)
  elseif n <= 0x7ff then
    return string.char(f(n / 64) + 192, n % 64 + 128)
  elseif n <= 0xffff then
    return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
  elseif n <= 0x10ffff then
    return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
                       f(n % 4096 / 64) + 128, n % 64 + 128)
  end
  error( string.format("invalid unicode codepoint '%x'", n) )
end


local function parse_unicode_escape(s)
  local n1 = tonumber( s:sub(3, 6),  16 )
  local n2 = tonumber( s:sub(9, 12), 16 )
  -- Surrogate pair?
  if n2 then
    return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
  else
    return codepoint_to_utf8(n1)
  end
end


local function parse_string(str, i)
  local has_unicode_escape = false
  local has_surrogate_escape = false
  local has_escape = false
  local last
  for j = i + 1, #str do
    local x = str:byte(j)

    if x < 32 then
      decode_error(str, j, "control character in string")
    end

    if last == 92 then -- "\\" (escape char)
      if x == 117 then -- "u" (unicode escape sequence)
        local hex = str:sub(j + 1, j + 5)
        if not hex:find("%x%x%x%x") then
          decode_error(str, j, "invalid unicode escape in string")
        end
        if hex:find("^[dD][89aAbB]") then
          has_surrogate_escape = true
        else
          has_unicode_escape = true
        end
      else
        local c = string.char(x)
        if not escape_chars[c] then
          decode_error(str, j, "invalid escape char '" .. c .. "' in string")
        end
        has_escape = true
      end
      last = nil

    elseif x == 34 then -- '"' (end of string)
      local s = str:sub(i + 1, j - 1)
      if has_surrogate_escape then
        s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
      end
      if has_unicode_escape then
        s = s:gsub("\\u....", parse_unicode_escape)
      end
      if has_escape then
        s = s:gsub("\\.", escape_char_map_inv)
      end
      return s, j + 1

    else
      last = x
    end
  end
  decode_error(str, i, "expected closing quote for string")
end


local function parse_number(str, i)
  local x = next_char(str, i, delim_chars)
  local s = str:sub(i, x - 1)
  local n = tonumber(s)
  if not n then
    decode_error(str, i, "invalid number '" .. s .. "'")
  end
  return n, x
end


local function parse_literal(str, i)
  local x = next_char(str, i, delim_chars)
  local word = str:sub(i, x - 1)
  if not literals[word] then
    decode_error(str, i, "invalid literal '" .. word .. "'")
  end
  return literal_map[word], x
end


local function parse_array(str, i)
  local res = {}
  local n = 1
  i = i + 1
  while 1 do
    local x
    i = next_char(str, i, space_chars, true)
    -- Empty / end of array?
    if str:sub(i, i) == "]" then
      i = i + 1
      break
    end
    -- Read token
    x, i = parse(str, i)
    res[n] = x
    n = n + 1
    -- Next token
    i = next_char(str, i, space_chars, true)
    local chr = str:sub(i, i)
    i = i + 1
    if chr == "]" then break end
    if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
  end
  return res, i
end


local function parse_object(str, i)
  local res = {}
  i = i + 1
  while 1 do
    local key, val
    i = next_char(str, i, space_chars, true)
    -- Empty / end of object?
    if str:sub(i, i) == "}" then
      i = i + 1
      break
    end
    -- Read key
    if str:sub(i, i) ~= '"' then
      decode_error(str, i, "expected string for key")
    end
    key, i = parse(str, i)
    -- Read ':' delimiter
    i = next_char(str, i, space_chars, true)
    if str:sub(i, i) ~= ":" then
      decode_error(str, i, "expected ':' after key")
    end
    i = next_char(str, i + 1, space_chars, true)
    -- Read value
    val, i = parse(str, i)
    -- Set
    res[key] = val
    -- Next token
    i = next_char(str, i, space_chars, true)
    local chr = str:sub(i, i)
    i = i + 1
    if chr == "}" then break end
    if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
  end
  return res, i
end


local char_func_map = {
  [ '"' ] = parse_string,
  [ "0" ] = parse_number,
  [ "1" ] = parse_number,
  [ "2" ] = parse_number,
  [ "3" ] = parse_number,
  [ "4" ] = parse_number,
  [ "5" ] = parse_number,
  [ "6" ] = parse_number,
  [ "7" ] = parse_number,
  [ "8" ] = parse_number,
  [ "9" ] = parse_number,
  [ "-" ] = parse_number,
  [ "t" ] = parse_literal,
  [ "f" ] = parse_literal,
  [ "n" ] = parse_literal,
  [ "[" ] = parse_array,
  [ "{" ] = parse_object,
}


parse = function(str, idx)
  local chr = str:sub(idx, idx)
  local f = char_func_map[chr]
  if f then
    return f(str, idx)
  end
  decode_error(str, idx, "unexpected character '" .. chr .. "'")
end


function json.decode(str)
  if type(str) ~= "string" then
    error("expected argument of type string, got " .. type(str))
  end
  local res, idx = parse(str, next_char(str, 1, space_chars, true))
  idx = next_char(str, idx, space_chars, true)
  if idx <= #str then
    decode_error(str, idx, "trailing garbage")
  end
  return res
end
 
Last edited:
Solution
I tried to link it and execute json.decode("[]") and it works fine.

1678135939933.png
1678135955313.png

EDIT: Yeaa I tried wrong file from upper post, yea the solution was not to define json as local 😄
 
Last edited:
Скриншот 07-03-2023 000714.jpg
thank you so much for the answers and hints. everything works fine without errors
Post automatically merged:

replacement with your json.lua helped
 
I'm going to use this post because I'm having trouble with this module. I've been trying to get this module to work for 5 days, but when I open the module, the tasks don't appear.

Tfs 1.4
Otcv8-dev

tasks.PNG

creaturescript/extendedopcode.lua

Lua:
local OPCODE_LANGUAGE = 1

function onExtendedOpcode(player, opcode, buffer)
    if opcode == OPCODE_LANGUAGE then
        -- otclient language
        if buffer == 'en' or buffer == 'pt' then
            -- example, setting player language, because otclient is multi-language...
            -- player:setStorageValue(SOME_STORAGE_ID, SOME_VALUE)
        end
    elseif opcode == 215 then
        TaskSystem.onAction(player, json.decode(buffer))   
    else
        -- other opcodes can be ignored, and the server will just work fine...
    end
end

lib/core/json.lua
Code:
--
-- json.lua
--
-- Copyright (c) 2018 rxi
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
--

json = { _version = "0.1.1" }

-------------------------------------------------------------------------------
-- Encode
-------------------------------------------------------------------------------

local encode

local escape_char_map = {
  [ "\\" ] = "\\\\",
  [ "\"" ] = "\\\"",
  [ "\b" ] = "\\b",
  [ "\f" ] = "\\f",
  [ "\n" ] = "\\n",
  [ "\r" ] = "\\r",
  [ "\t" ] = "\\t",
}

local escape_char_map_inv = { [ "\\/" ] = "/" }
for k, v in pairs(escape_char_map) do
  escape_char_map_inv[v] = k
end


local function escape_char(c)
  return escape_char_map[c] or string.format("\\u%04x", c:byte())
end


local function encode_nil(val)
  return "null"
end


local function encode_table(val, stack)
  local res = {}
  stack = stack or {}

  -- Circular reference?
  if stack[val] then error("circular reference") end

  stack[val] = true

  if val[1] ~= nil or next(val) == nil then
    -- Treat as array -- check keys are valid and it is not sparse
    local n = 0
    for k in pairs(val) do
      if type(k) ~= "number" then
        error("invalid table: mixed or invalid key types")
      end
      n = n + 1
    end
    if n ~= #val then
      error("invalid table: sparse array")
    end
    -- Encode
    for i, v in ipairs(val) do
      table.insert(res, encode(v, stack))
    end
    stack[val] = nil
    return "[" .. table.concat(res, ",") .. "]"

  else
    -- Treat as an object
    for k, v in pairs(val) do
      if type(k) ~= "string" then
        error("invalid table: mixed or invalid key types")
      end
      table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
    end
    stack[val] = nil
    return "{" .. table.concat(res, ",") .. "}"
  end
end


local function encode_string(val)
  return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
end


local function encode_number(val)
  -- Check for NaN, -inf and inf
  if val ~= val or val <= -math.huge or val >= math.huge then
    error("unexpected number value '" .. tostring(val) .. "'")
  end
  return string.format("%.14g", val)
end


local type_func_map = {
  [ "nil"     ] = encode_nil,
  [ "table"   ] = encode_table,
  [ "string"  ] = encode_string,
  [ "number"  ] = encode_number,
  [ "boolean" ] = tostring,
}


encode = function(val, stack)
  local t = type(val)
  local f = type_func_map[t]
  if f then
    return f(val, stack)
  end
  error("unexpected type '" .. t .. "'")
end


function json.encode(val)
  return ( encode(val) )
end


-------------------------------------------------------------------------------
-- Decode
-------------------------------------------------------------------------------

local parse

local function create_set(...)
  local res = {}
  for i = 1, select("#", ...) do
    res[ select(i, ...) ] = true
  end
  return res
end

local space_chars   = create_set(" ", "\t", "\r", "\n")
local delim_chars   = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
local escape_chars  = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
local literals      = create_set("true", "false", "null")

local literal_map = {
  [ "true"  ] = true,
  [ "false" ] = false,
  [ "null"  ] = nil,
}


local function next_char(str, idx, set, negate)
  for i = idx, #str do
    if set[str:sub(i, i)] ~= negate then
      return i
    end
  end
  return #str + 1
end


local function decode_error(str, idx, msg)
  local line_count = 1
  local col_count = 1
  for i = 1, idx - 1 do
    col_count = col_count + 1
    if str:sub(i, i) == "\n" then
      line_count = line_count + 1
      col_count = 1
    end
  end
  error( string.format("%s at line %d col %d", msg, line_count, col_count) )
end


local function codepoint_to_utf8(n)
  -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
  local f = math.floor
  if n <= 0x7f then
    return string.char(n)
  elseif n <= 0x7ff then
    return string.char(f(n / 64) + 192, n % 64 + 128)
  elseif n <= 0xffff then
    return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
  elseif n <= 0x10ffff then
    return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
                       f(n % 4096 / 64) + 128, n % 64 + 128)
  end
  error( string.format("invalid unicode codepoint '%x'", n) )
end


local function parse_unicode_escape(s)
  local n1 = tonumber( s:sub(3, 6),  16 )
  local n2 = tonumber( s:sub(9, 12), 16 )
  -- Surrogate pair?
  if n2 then
    return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
  else
    return codepoint_to_utf8(n1)
  end
end


local function parse_string(str, i)
  local has_unicode_escape = false
  local has_surrogate_escape = false
  local has_escape = false
  local last
  for j = i + 1, #str do
    local x = str:byte(j)

    if x < 32 then
      decode_error(str, j, "control character in string")
    end

    if last == 92 then -- "\\" (escape char)
      if x == 117 then -- "u" (unicode escape sequence)
        local hex = str:sub(j + 1, j + 5)
        if not hex:find("%x%x%x%x") then
          decode_error(str, j, "invalid unicode escape in string")
        end
        if hex:find("^[dD][89aAbB]") then
          has_surrogate_escape = true
        else
          has_unicode_escape = true
        end
      else
        local c = string.char(x)
        if not escape_chars[c] then
          decode_error(str, j, "invalid escape char '" .. c .. "' in string")
        end
        has_escape = true
      end
      last = nil

    elseif x == 34 then -- '"' (end of string)
      local s = str:sub(i + 1, j - 1)
      if has_surrogate_escape then
        s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
      end
      if has_unicode_escape then
        s = s:gsub("\\u....", parse_unicode_escape)
      end
      if has_escape then
        s = s:gsub("\\.", escape_char_map_inv)
      end
      return s, j + 1

    else
      last = x
    end
  end
  decode_error(str, i, "expected closing quote for string")
end


local function parse_number(str, i)
  local x = next_char(str, i, delim_chars)
  local s = str:sub(i, x - 1)
  local n = tonumber(s)
  if not n then
    decode_error(str, i, "invalid number '" .. s .. "'")
  end
  return n, x
end


local function parse_literal(str, i)
  local x = next_char(str, i, delim_chars)
  local word = str:sub(i, x - 1)
  if not literals[word] then
    decode_error(str, i, "invalid literal '" .. word .. "'")
  end
  return literal_map[word], x
end


local function parse_array(str, i)
  local res = {}
  local n = 1
  i = i + 1
  while 1 do
    local x
    i = next_char(str, i, space_chars, true)
    -- Empty / end of array?
    if str:sub(i, i) == "]" then
      i = i + 1
      break
    end
    -- Read token
    x, i = parse(str, i)
    res[n] = x
    n = n + 1
    -- Next token
    i = next_char(str, i, space_chars, true)
    local chr = str:sub(i, i)
    i = i + 1
    if chr == "]" then break end
    if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
  end
  return res, i
end


local function parse_object(str, i)
  local res = {}
  i = i + 1
  while 1 do
    local key, val
    i = next_char(str, i, space_chars, true)
    -- Empty / end of object?
    if str:sub(i, i) == "}" then
      i = i + 1
      break
    end
    -- Read key
    if str:sub(i, i) ~= '"' then
      decode_error(str, i, "expected string for key")
    end
    key, i = parse(str, i)
    -- Read ':' delimiter
    i = next_char(str, i, space_chars, true)
    if str:sub(i, i) ~= ":" then
      decode_error(str, i, "expected ':' after key")
    end
    i = next_char(str, i + 1, space_chars, true)
    -- Read value
    val, i = parse(str, i)
    -- Set
    res[key] = val
    -- Next token
    i = next_char(str, i, space_chars, true)
    local chr = str:sub(i, i)
    i = i + 1
    if chr == "}" then break end
    if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
  end
  return res, i
end


local char_func_map = {
  [ '"' ] = parse_string,
  [ "0" ] = parse_number,
  [ "1" ] = parse_number,
  [ "2" ] = parse_number,
  [ "3" ] = parse_number,
  [ "4" ] = parse_number,
  [ "5" ] = parse_number,
  [ "6" ] = parse_number,
  [ "7" ] = parse_number,
  [ "8" ] = parse_number,
  [ "9" ] = parse_number,
  [ "-" ] = parse_number,
  [ "t" ] = parse_literal,
  [ "f" ] = parse_literal,
  [ "n" ] = parse_literal,
  [ "[" ] = parse_array,
  [ "{" ] = parse_object,
}


parse = function(str, idx)
  local chr = str:sub(idx, idx)
  local f = char_func_map[chr]
  if f then
    return f(str, idx)
  end
  decode_error(str, idx, "unexpected character '" .. chr .. "'")
end


function json.decode(str)
  if type(str) ~= "string" then
    error("expected argument of type string, got " .. type(str))
  end
  local res, idx = parse(str, next_char(str, 1, space_chars, true))
  idx = next_char(str, idx, space_chars, true)
  if idx <= #str then
    decode_error(str, idx, "trailing garbage")
  end
  return res
end

lib/core/player.lua
Code:
local foodCondition = Condition(CONDITION_REGENERATION, CONDITIONID_DEFAULT)

function Player.feed(self, food)
    local condition = self:getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT)
    if condition then
        condition:setTicks(condition:getTicks() + (food * 1000))
    else
        local vocation = self:getVocation()
        if not vocation then
            return nil
        end

        foodCondition:setTicks(food * 1000)
        foodCondition:setParameter(CONDITION_PARAM_HEALTHGAIN, vocation:getHealthGainAmount())
        foodCondition:setParameter(CONDITION_PARAM_HEALTHTICKS, vocation:getHealthGainTicks() * 1000)
        foodCondition:setParameter(CONDITION_PARAM_MANAGAIN, vocation:getManaGainAmount())
        foodCondition:setParameter(CONDITION_PARAM_MANATICKS, vocation:getManaGainTicks() * 1000)

        self:addCondition(foodCondition)
    end
    return true
end

function Player.getClosestFreePosition(self, position, extended)
    if self:getGroup():getAccess() and self:getAccountType() >= ACCOUNT_TYPE_GOD then
        return position
    end
    return Creature.getClosestFreePosition(self, position, extended)
end

function Player.getDepotItems(self, depotId)
    return self:getDepotChest(depotId, true):getItemHoldingCount()
end

function Player.hasFlag(self, flag)
    return self:getGroup():hasFlag(flag)
end

function Player.getLossPercent(self)
    local blessings = 0
    local lossPercent = {
        [0] = 100,
        [1] = 70,
        [2] = 45,
        [3] = 25,
        [4] = 10,
        [5] = 0
    }

    for i = 1, 5 do
        if self:hasBlessing(i) then
            blessings = blessings + 1
        end
    end
    return lossPercent[blessings]
end

function Player.sendExtendedJSONOpcode(self, opcode, buffer)
    if not self:isUsingOtClient() then
        return false
    end

    local networkMessage = NetworkMessage()
    networkMessage:addByte(0x32)
    networkMessage:addByte(opcode)
    networkMessage:addString(json.encode(buffer))
    networkMessage:sendToPlayer(self)
    networkMessage:delete()
    return true
end

function Player.getPremiumTime(self)
    return math.max(0, self:getPremiumEndsAt() - os.time())
end

function Player.setPremiumTime(self, seconds)
    self:setPremiumEndsAt(os.time() + seconds)
    return true
end

function Player.addPremiumTime(self, seconds)
    self:setPremiumTime(self:getPremiumTime() + seconds)
    return true
end

function Player.removePremiumTime(self, seconds)
    local currentTime = self:getPremiumTime()
    if currentTime < seconds then
        return false
    end

    self:setPremiumTime(currentTime - seconds)
    return true
end

function Player.getPremiumDays(self)
    return math.floor(self:getPremiumTime() / 86400)
end

function Player.addPremiumDays(self, days)
    return self:addPremiumTime(days * 86400)
end

function Player.removePremiumDays(self, days)
    return self:removePremiumTime(days * 86400)
end

function Player.isPremium(self)
    return self:getPremiumTime() > 0 or configManager.getBoolean(configKeys.FREE_PREMIUM) or self:hasFlag(PlayerFlag_IsAlwaysPremium)
end

function Player.sendCancelMessage(self, message)
    if type(message) == "number" then
        message = Game.getReturnMessage(message)
    end
    return self:sendTextMessage(MESSAGE_STATUS_SMALL, message)
end

function Player.isUsingOtClient(self)
    return self:getClient().os >= CLIENTOS_OTCLIENT_LINUX
end

function Player.sendExtendedOpcode(self, opcode, buffer)
    if not self:isUsingOtClient() then
        return false
    end

    local networkMessage = NetworkMessage()
    networkMessage:addByte(0x32)
    networkMessage:addByte(opcode)
    networkMessage:addString(buffer)
    networkMessage:sendToPlayer(self)
    networkMessage:delete()
    return true
end


APPLY_SKILL_MULTIPLIER = true
local addSkillTriesFunc = Player.addSkillTries
function Player.addSkillTries(...)
    APPLY_SKILL_MULTIPLIER = false
    local ret = addSkillTriesFunc(...)
    APPLY_SKILL_MULTIPLIER = true
    return ret
end

local addManaSpentFunc = Player.addManaSpent
function Player.addManaSpent(...)
    APPLY_SKILL_MULTIPLIER = false
    local ret = addManaSpentFunc(...)
    APPLY_SKILL_MULTIPLIER = true
    return ret
end

-- Always pass the number through the isValidMoney function first before using the transferMoneyTo
function Player.transferMoneyTo(self, target, amount)
    if not target then
        return false
    end

    -- See if you can afford this transfer
    local balance = self:getBankBalance()
    if amount > balance then
        return false
    end

    -- See if player is online
    local targetPlayer = Player(target.guid)
    if targetPlayer then
        targetPlayer:setBankBalance(targetPlayer:getBankBalance() + amount)
    else
        db.query("UPDATE `players` SET `balance` = `balance` + " .. amount .. " WHERE `id` = '" .. target.guid .. "'")
    end

    self:setBankBalance(self:getBankBalance() - amount)
    return true
end

function Player.canCarryMoney(self, amount)
    -- Anyone can carry as much imaginary money as they desire
    if amount == 0 then
        return true
    end

    -- The 3 below loops will populate these local variables
    local totalWeight = 0
    local inventorySlots = 0

    -- Add crystal coins to totalWeight and inventorySlots
    local type_crystal = ItemType(ITEM_CRYSTAL_COIN)
    local crystalCoins = math.floor(amount / 10000)
    if crystalCoins > 0 then
        amount = amount - (crystalCoins * 10000)
        while crystalCoins > 0 do
            local count = math.min(100, crystalCoins)
            totalWeight = totalWeight + type_crystal:getWeight(count)
            crystalCoins = crystalCoins - count
            inventorySlots = inventorySlots + 1
        end
    end

    -- Add platinum coins to totalWeight and inventorySlots
    local type_platinum = ItemType(ITEM_PLATINUM_COIN)
    local platinumCoins = math.floor(amount / 100)
    if platinumCoins > 0 then
        amount = amount - (platinumCoins * 100)
        while platinumCoins > 0 do
            local count = math.min(100, platinumCoins)
            totalWeight = totalWeight + type_platinum:getWeight(count)
            platinumCoins = platinumCoins - count
            inventorySlots = inventorySlots + 1
        end
    end

    -- Add gold coins to totalWeight and inventorySlots
    local type_gold = ItemType(ITEM_GOLD_COIN)
    if amount > 0 then
        while amount > 0 do
            local count = math.min(100, amount)
            totalWeight = totalWeight + type_gold:getWeight(count)
            amount = amount - count
            inventorySlots = inventorySlots + 1
        end
    end

    -- If player don't have enough capacity to carry this money
    if self:getFreeCapacity() < totalWeight then
        return false
    end

    -- If player don't have enough available inventory slots to carry this money
    local backpack = self:getSlotItem(CONST_SLOT_BACKPACK)
    if not backpack or backpack:getEmptySlots(true) < inventorySlots then
        return false
    end
    return true
end

function Player.withdrawMoney(self, amount)
    local balance = self:getBankBalance()
    if amount > balance or not self:addMoney(amount) then
        return false
    end

    self:setBankBalance(balance - amount)
    return true
end

function Player.depositMoney(self, amount)
    if not self:removeMoney(amount) then
        return false
    end

    self:setBankBalance(self:getBankBalance() + amount)
    return true
end

function Player.removeTotalMoney(self, amount)
    local moneyCount = self:getMoney()
    local bankCount = self:getBankBalance()
    if amount <= moneyCount then
        self:removeMoney(amount)
        return true
    elseif amount <= (moneyCount + bankCount) then
        if moneyCount ~= 0 then
            self:removeMoney(moneyCount)
            local remains = amount - moneyCount
            self:setBankBalance(bankCount - remains)
            self:sendTextMessage(MESSAGE_INFO_DESCR, ("Paid %d from inventory and %d gold from bank account. Your account balance is now %d gold."):format(moneyCount, amount - moneyCount, self:getBankBalance()))
            return true
        else
            self:setBankBalance(bankCount - amount)
            self:sendTextMessage(MESSAGE_INFO_DESCR, ("Paid %d gold from bank account. Your account balance is now %d gold."):format(amount, self:getBankBalance()))
            return true
        end
    end
    return false
end

function Player.addLevel(self, amount, round)
    round = round or false
    local level, amount = self:getLevel(), amount or 1
    if amount > 0 then
        return self:addExperience(Game.getExperienceForLevel(level + amount) - (round and self:getExperience() or Game.getExperienceForLevel(level)))
    else
        return self:removeExperience(((round and self:getExperience() or Game.getExperienceForLevel(level)) - Game.getExperienceForLevel(level + amount)))
    end
end

function Player.addMagicLevel(self, value)
    local currentMagLevel = self:getBaseMagicLevel()
    local sum = 0

    if value > 0 then
        while value > 0 do
            sum = sum + self:getVocation():getRequiredManaSpent(currentMagLevel + value)
            value = value - 1
        end

        return self:addManaSpent(sum - self:getManaSpent())
    else
        value = math.min(currentMagLevel, math.abs(value))
        while value > 0 do
            sum = sum + self:getVocation():getRequiredManaSpent(currentMagLevel - value + 1)
            value = value - 1
        end

        return self:removeManaSpent(sum + self:getManaSpent())
    end
end

function Player.addSkillLevel(self, skillId, value)
    local currentSkillLevel = self:getSkillLevel(skillId)
    local sum = 0

    if value > 0 then
        while value > 0 do
            sum = sum + self:getVocation():getRequiredSkillTries(skillId, currentSkillLevel + value)
            value = value - 1
        end

        return self:addSkillTries(skillId, sum - self:getSkillTries(skillId))
    else
        value = math.min(currentSkillLevel, math.abs(value))
        while value > 0 do
            sum = sum + self:getVocation():getRequiredSkillTries(skillId, currentSkillLevel - value + 1)
            value = value - 1
        end

        return self:removeSkillTries(skillId, sum + self:getSkillTries(skillId), true)
    end
end

function Player.addSkill(self, skillId, value, round)
    if skillId == SKILL_LEVEL then
        return self:addLevel(value, round or false)
    elseif skillId == SKILL_MAGLEVEL then
        return self:addMagicLevel(value)
    end
    return self:addSkillLevel(skillId, value)
end

function Player.getWeaponType(self)
    local weapon = self:getSlotItem(CONST_SLOT_LEFT)
    if weapon then
        return weapon:getType():getWeaponType()
    end
    return WEAPON_NONE
end

function Player.getTotalMoney(self)
    return self:getMoney() + self:getBankBalance()
end

script/taskSystem.lua is the same as in the post, I just added a print statement.

No error in TFS console.
No error in Otcv8 console.


Print:
print task.PNG
print task2.PNG

If you want to help me, please.
 
It's easy, just use storage 5151 at the NPC.
I even tried, but the script I have is in modal and my test server has no modal window option. Could you help me convert to a modal-less version?
Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}
function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end
function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end
function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end
function onThink() npcHandler:onThink() end
function creatureSayCallback(cid, type, msg)
if(not npcHandler:isFocused(cid)) then
return false
end
local talkUser = NPCHANDLER_CONVbehavior == CONVERSATION_DEFAULT and 0 or cid
local shopWindow = {}
local t = {
 [2160] = {quant = 15},
 [11447] = {quant = 13},
 [12345] = {quant = 1}
 }
 local storage = 5151
local onBuy = function(cid, item, subType, amount, ignoreCap, inBackpacks)
 if getPlayerStorageValue(cid, storage) >= t[item].quant then
selfSay("You change "..t[item].quant.." storage for {"..getItemNameById(item).."}", cid)
doPlayerAddItem(cid,item,1)
setPlayerStorageValue(cid, storage, getPlayerStorageValue(cid, storage) - t[item].quant)
else
selfSay("You no have "..t[item].quant.. " storage for change {"..getItemNameById(item).."}", cid)
  end
return true
end
if (msgcontains(msg, 'trade') or msgcontains(msg, 'TRADE'))then
for var, ret in pairs(t) do
table.insert(shopWindow, {id = var, subType = 0, buy = ret.quant, sell = 0, name = getItemNameById(var)})
end
openShopWindow(cid, shopWindow, onBuy, onSell)
end
return true
end
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())
 
Take this as a basis, I haven't tested it.

Lua:
 if msgcontains(msg, "demon armor") then
        selfSay("Would you like to buy a Demon Armor for 300 task points?", cid)
        talkState[talkUser] = 1
    elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
        if getPlayerStorageValue(cid, 5151) => 300 then
            doPlayerAddItem(cid, 2494, 1)
            setPlayerStorageValue(cid, 5151, (getPlayerStorageValue(cid, 5151) - 300))
            selfSay("Here it is.", cid)
        else
            selfSay("You don't have it.", cid)
        end
     
        talkState[talkUser] = 0
    elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
        selfSay("Ok", cid)
        talkState[talkUser] = 0
        npcHandler:releaseFocus(cid)
    end
    return true
end
 
Back
Top