• 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!

[TUTORIAL MODS]- Creating Modules with Extended Opcode

Ascuas Funkeln

Rakkedo Game
Joined
Apr 14, 2013
Messages
549
Solutions
32
Reaction score
305
Location
Poland
GitHub
AscuasFunkeln
Hello, here is my little tutorial how create things in otclient to control server side actions.
Im not super experienced in this case and my explain of it is pretty lame :D , anyway i will show how I came to how to do it and how I do it (something is wrong with that phrase xD)

Ok, let's start!

What you need:
OTClient
TFS 1.x or any other with availabe ExtendedOpcodes

Step 1
Create new folder in "Otclient/mods/" called "food_eater"
1.JPG

Step 2
Copy three basic files from any other module from "Otclient/modules", *.lua, *.otui and *.otmod and rename it to "food_eater"
2.JPG
Step 3

First open food_eater.otmod, you will get here some information about module and "link" to load it, let me explain:
Code:
Module
  name: food_eater
  description: Food Eater
  author: Otland for the Win
  website: -
  version: 1

  autoload: true
  autoload-priority: 1000
  sandboxed: true
  dependencies: [ game_interface ]
  scripts: [ food_eater ]
  @onLoad: init()
  @onUnload: terminate()

Ofc first sector is pretty logical, anyway try keep module name same like files name.
Most important line is " scripts: [ food_eater ]" this define what script files will be loaded with module.
Ok, copy this prepared code and lets go to next step.

Step 4

Create inside next folder called "Otclient/mods/food_eater/food_eater"
3.JPG
and paste inside this folder icon in 24x24 and transparency saved as .png
Like this :D
1587577967113.png

Step 5

Lua:
foodeaterButton = nil
foodeaterWindow = nil


function init()
  foodeaterButton = modules.client_topmenu.addRightToggleButton('foodeaterButton', tr('Food Eater'), '/food_eater/food_eater/food_eater', closing)
  foodeaterButton:setOn(false)

  foodeaterWindow = g_ui.displayUI('food_eater')
  foodeaterWindow:setVisible(false)

  --get help values
  allTabs = foodeaterWindow:recursiveGetChildById('allTabs')
  allTabs:setContentWidget(foodeaterWindow:getChildById('optionsTabContent'))

end

function terminate()
  foodeaterButton:destroy()
  foodeaterWindow:destroy()
end

function closing()
  if foodeaterButton:isOn() then
    foodeaterWindow:setVisible(false)
    foodeaterButton:setOn(false)
  else
    foodeaterWindow:setVisible(true)
    foodeaterButton:setOn(true)
  end
end
function banana()
local protocolGame = g_game.getProtocolGame()
  if foodeaterButton:isOn() then
  if protocolGame then
      protocolGame:sendExtendedOpcode(14, 1)
    end
  else
    foodeaterWindow:setVisible(true)
    foodeaterButton:setOn(true)
  end
end

function melon()
local protocolGame = g_game.getProtocolGame()
  if foodeaterButton:isOn() then
  if protocolGame then
      protocolGame:sendExtendedOpcode(14, 2)
    end
  else
    foodeaterWindow:setVisible(true)
    foodeaterButton:setOn(true)
  end
end
function cake()
local protocolGame = g_game.getProtocolGame()
  if foodeaterButton:isOn() then
  if protocolGame then
      protocolGame:sendExtendedOpcode(14, 3)
    end
  else
    foodeaterWindow:setVisible(true)
    foodeaterButton:setOn(true)
  end
end
function onMiniWindowClose()
  foodeaterButton:setOn(false)

end
Ok, there we will called our window and button as "foodeater"
Code:
foodeaterButton = nil
foodeaterWindow = nil
Easy you see any Window or Button have prefix foodeater, simple.
Below is line thats will create you own Window, call it food_eater, it link with food_eater.otui
Code:
  foodeaterWindow = g_ui.displayUI('food_eater')

function closing() - This function define what happen when you close module, we wil assign close button to this fuction
This two lines we can copy and paste into "other" buttons when we want, to close module when click button.
foodeaterWindow:setVisible(false)
foodeaterButton:setOn(false)


Now we will prepare "functionaly button script"
Lua:
function banana()
local protocolGame = g_game.getProtocolGame()
  if foodeaterButton:isOn() then
  if protocolGame then
      protocolGame:sendExtendedOpcode(14, 1)
    end
  else
    foodeaterWindow:setVisible(true)
    foodeaterButton:setOn(true)
  end
end
Lets call it banana,
local protocolGame = g_game.getProtocolGame() - You need to declare it in function, thats script need it to work with ExtendedOpcodes
if foodeaterButton:isOn() then - Define it when we click Button called Banana then script will be executed
protocolGame:sendExtendedOpcode(14, 1) - Now client send ExtendedOpcode to server with these values
Now we can copy this function banana and make some others like melon, cake and set different values of opcode.
Copy prepared code and paste it in food_eater.lua
ExtendedOpcodes Noob Explain: It similar to storages, opcode number 14 and value 1. And this values are sent - Server->Client and vice versa.

Step 6

Now we will open food_eater.otui


Code:
MainWindow
  id: foodeaterMain
  !text: tr('Food Eater')
  size: 235 215
  @onEscape: modules.food_eater.closing()

  TabBar
    id: allTabs
    anchors.top: parent.top
    anchors.right: parent.right
    anchors.bottom: parent.bottom
    anchors.left: parent.left

  Panel
    id: optionsTabContent
    anchors.top: allTabs.top
    anchors.left: allTabs.left
    anchors.right: allTabs.right
    anchors.bottom: allTabs.bottom
    margin-top: 20
    margin-bottom: 20

  Button
    id: Banana
    !text: tr('Banana')
    width: 200
    anchors.left: parent.left
    anchors.top: Melon.bottom
    @onClick: modules.food_eater.banana()

  Button
    id: Melon
    !text: tr('Melon')
    width: 200
    anchors.left: parent.left
    anchors.top: Cake.bottom
    @onClick: modules.food_eater.melon()

  Button
    id: Cake
    !text: tr('Cake')
    width: 200
    anchors.left: parent.left
    anchors.top: parent.top
    @onClick: modules.food_eater.cake()

  Button
    id: closeButton
    !text: tr('Close')
    width: 200
    anchors.left: parent.left
    anchors.bottom: parent.bottom
    @onClick: modules.food_eater.closing()
Now

id: foodeaterMain
!text: tr('Food Eater')
size: 235 215 -- Here you define size of the window
@onEscape: modules.food_eater.closing()

Lets focus on buttons, banana one:

Button
id: Banana -- call it Banana
!text: tr('Banana') -- Display text
width: 200 -- Widht of button compare it with window size
anchors.left: parent.left
anchors.top: Melon.bottom -- Before or after what button it will display
@onClick: modules.food_eater.banana() -- IMPORTANT, here you define with what function in food_eater.lua button will triggered

Step 7

Ok, lets now prepare some server stuff.
In data/creaturescript/creaturescript.xml
add this
<event type="extendedopcode" name="foodeater" script="foodeater.lua" />
In login.lua
player:registerEvent("foodeater")
then make foodeater.lua
and paste this:
Lua:
function onExtendedOpcode(player, opcode, buffer)
    local banana = player:getItemCount(2676)
        local cake = player:getItemCount(6278)
            local melon = player:getItemCount(2680)
                  local buf = tonumber(buffer)
  if opcode == 14 and buf == 1 and banana >= 1 then

    player:feed(180)
            player:say("Omn, omn~! Banani!", TALKTYPE_MONSTER_SAY)
        player:removeItem(2676, 1)
        elseif opcode == 14 and buf == 3 and cake >= 1 then

    player:feed(180)
            player:say("Omn, omn~! Fat Boosting!", TALKTYPE_MONSTER_SAY)
        player:removeItem(6278, 1)

                elseif opcode == 14 and buf == 2 and melon >= 1 then
        player:removeItem(2680, 1)
    player:feed(180)
            player:say("Omn, omn~! He?!", TALKTYPE_MONSTER_SAY)
        end
        end

Did i need explain server side code? :D
if opcode == 14 and buf == 1 and banana >= 1 then (Simple: if Opcode number 14, have value 1, and player have 1 or more bananas, this will be removed and food time will be addeded.

OK, its my first tutorial, hope this will be helpfull xD

Let's work togheter!
 

Attachments

Because extended op codes are accepting only strings, using JSON is very helpful to send more data.
Download and drop json.lua file into otclient/modules/corelib, then open corelib.otmod in that folder and after
Code:
    dofile 'outputmessage'
add
Code:
    dofile 'json'

Now go to forgottenserver\data\lib\core and drop that file there, open core.lua and add
Lua:
dofile('data/lib/core/json.lua')

Usage:
In OTC modules
Lua:
function sendMyCode()
    local myData = {
        a = "string",
        b = 123,
        c = {
            x = "string in table",
            y = 456
        }
    }
    protocolGame.sendExtendedOpCode(14, json.encode(myData))
end

TFS
Lua:
function onExtendedOpcode(player, opcode, buffer)
    local status, json_data =
        pcall(
            function()
                return json.decode(buffer)
            end
        )
    if not status then
        return false
    end
    
    print(json_data.a)
    print(json_data.b)
    print(json_data.c.x)
    print(json_data.c.y)
end

Good luck!
 

Attachments

Because extended op codes are accepting only strings, using JSON is very helpful to send more data.
Download and drop json.lua file into otclient/modules/corelib, then open corelib.otmod in that folder and after
Code:
    dofile 'outputmessage'
add
Code:
    dofile 'json'

Now go to forgottenserver\data\lib\core and drop that file there, open core.lua and add
Lua:
dofile('data/lib/core/json.lua')

Usage:
In OTC modules
Lua:
function sendMyCode()
    local myData = {
        a = "string",
        b = 123,
        c = {
            x = "string in table",
            y = 456
        }
    }
    protocolGame.sendExtendedOpCode(14, json.encode(myData))
end

TFS
Lua:
function onExtendedOpcode(player, opcode, buffer)
    local status, json_data =
        pcall(
            function()
                return json.decode(buffer)
            end
        )
    if not status then
        return false
    end
   
    print(json_data.a)
    print(json_data.b)
    print(json_data.c.x)
    print(json_data.c.y)
end

Good luck!
Thats was nice, knowledge upgraded :D
 
I did everything in the tutorial and I get this error:
ERROR: Unable to send extended opcode 14, extended opcodes are not enabled
 
Do you have extended op codes enabled in features.lua?
 
In OTClient right?
I don't have "features.lua" then i added this g_game.enableFeature(GameExtendedOpcode) into game_things/things.lua (in function load() )
 
OTClient 1.0 mehah
What server? TFS? Which version?
 
Back
Top