LibDataPacker is a Lua library for efficiently packing and unpacking data structures into compact binary representations. It provides:
- Strongly typed schema definitions
- Efficient serialization
- Support for various data types
- Configurable bit-level precision
Numeric
Field.Number(name, bitLength, precision)- name: Field name, optional
- bitLength: Number of bits to use
- precision: Decimal precision, optional
Creates a field for storing numbers. The bitLength defines the maximum storable value as 2^bitLength - 1. For example, a numeric field with bitLength = 10 can store numbers from 0 to 1023 (since 2^10 - 1 = 1023).
The precision parameter controls number rounding:
- If omitted, the field will store integer values only
- If precision > 0: Specifies the number of decimal places to preserve
- If precision < 0: Specifies rounding strength (step size)
local n1 = Field.Number(nil, 10) -- Integer range: 0 - 1023
local n2 = Field.Number(nil, 10, 2) -- Decimal range: 0.00 - 10.23 (2 decimal places)
local n3 = Field.Number(nil, 10, -2) -- Scaled integer range: 0 - 102300 (steps of 100)Boolean
Field.Bool(name)Represents a true/false value in a single bit.
- name: Field name, optional
local someFlag = Field.Bool('flagName') -- true/falseString
Field.String(name, maxLength)- name: Field name, optional
- maxLength: Maximum expected string length
local name = Field.String('characterName', 25) -- SuperCharacterNamePKEnum
Field.Enum(name, enumTable, inverted)- name: Field name, optional
- enumTable: Table mapping values to numeric IDs
- inverted: If true, table is {value = name} instead of {name = value}
Simple {name - value} table.
local enum = {
['one'] = 1,
['two'] = 2,
['three'] = 3,
}
local invertedEnum = {
[1] = 'one',
[2] = 'two',
[3] = 'three,
}
local someEnum1 = Field.Enum(nil, enum)
local someEnum2 = Field.Enum(nil, invertedEnum, true)Array
Field.Array(name, length, elementType) -- Fixed length- name: Field name, optional
- length: Array length
- elementType: type of array elements (can be any
Field)
Indexed table with strictly defined amount of elements.
local id = Field.Number(nil, 20)
local array = Field.Array(nil, 5, id)
-- array of 5 id's:
local data = {
111111,
222222,
333333,
444444,
555555,
}Variable Length Array
Field.VLArray(name, maxLength, elementType) -- Variable length- name: Field name, optional
- maxLength: Max expected length
- elementType: type of array elements
Same as Array, but can contain from 0 to maxLength elements.
Table (Scheme)
Field.Table(name, fields, ignoreNames)- name: Field name, optional
- fields: Table with type of every field
- ignoreNames: If true, uses array indices instead of field names, default
false, optional
Represents table of other fields, almost always can be used as root Field in shemas.
local id = Field.Number(nil, 20)
local someEnum1 = Field.Enum(nil, enum)
local name = Field.String('characterName', 25)
local IGNORE_NAMES = true
local mySchema = Field.Table(nil, {
id,
enum,
name,
}, IGNORE_NAMES)
-- or
local mySchema = Field.Table(nil, {
Field.Number(nil, 20),
Field.Enum(nil, enum),
Field.String(nil, 25)
}, IGNORE_NAMES)
-- data example:
local someData = {
123456, -- id
3, -- enum
"SomeSuperNamePK", -- name
}LibDataPacker.Pack(data, schema, base)Serializes data according to schema.
- data: Input data table
- schema: Field definition
- base: Encoding base (default: Base64)
LibDataPacker.Unpack(packedData, schema, base)Deserializes packed data.
- packedData: Input packed data
- schema: Field definition
- base: Encoding base (default: Base64)
This is just an example to show what is possible. All values are arbitrary and can vary from real in-game values you should actually use.
Let's assume we want to save an item with an id, quality, trait, and enchantment id. That means we need 4 fields:
- id: number from 1 to 1M, so we should create a
Numberfield with a bit length of 20 (2^20-1 = 1,048,575, which covers the full range 1–1M) - quality: number from 1 to 6, so the bit length must be 3 (
2^3-1 = 7) - trait: number with a maximum of 54, so the closest bit length is 6 (
2^6-1 = 63) - enchantment id: number, bit length 20 (max =
1,048,575)
The item schema itself is a Table field with names ignored:
local IGNORE_NAMES = true
-- Item schema
local item = Field.Table('item', {
Field.Number('id', 20),
Field.Number('quality', 3),
Field.Number('trait', 6),
Field.Number('enchantmentId', 20),
}, IGNORE_NAMES)This means we must provide an indexed table to the packer like this:
local item1 = {
[1] = 123456, -- id
[2] = 7, -- quality
[3] = 8, -- trait
[4] = 901234 -- enchantment id
}
-- or
local item2 = {
987654, -- id
3, -- quality
2, -- trait
109876 -- enchantment id
}We could set IGNORE_NAMES = false, but that would require a table with named indices (not recommended, as this type of table is slightly slower, which could impact encoding/decoding performance):
local item1 = {
id = 123456,
quality = 7,
trait = 8,
enchantmentId = 901234
}
-- or
local item2 = {
['id'] = 987654,
['quality'] = 3,
['trait'] = 2,
['enchantmentId'] = 109876
}For the rest of this example, names will be ignored.
Next, we define a character with a name and an inventory (2 fields):
- name: string, max length 25 (according to ESO Help)
- inventory: array of items with length 20 (an array is an indexed table)
-- Character schema
local charSchema = Field.Table(nil, {
Field.String('name', 25),
Field.Array('inventory', 20, item),
}, IGNORE_NAMES)Valid character data might look like this:
local myChar = {
"Some name", -- name
{ -- inventory
item1,
item2,
{987654, 3, 2, 109876},
-- ... 17 more items
}
}That's all! We can now pack and unpack our data like this:
local packed = LibDataPacker.Pack(myChar, charSchema)
-- returns string in Base64 (by default), like this:
-- j32892489n2jklsoojgfj902iono2kpm23oin4jkvlksdmifiv2novx23i0n
local unpacked = LibDataPacker.Unpack(packed, charSchema)
-- returns the initial table
-- {
-- "Some name",
-- {
-- {123456, 7, 8, 901234},
-- {987654, 3, 2, 109876},
-- {987654, 3, 2, 109876},
-- ... 17 more items
-- }
-- }Please refer to extra/build/main.lua for more advanced example.
This is an separate module which can collect all data about character build (skills, gear, food, CP, etc.) and pack to compact string for storing or transmission.
local Build = LibDataPacker.Extra.Build
Build.GetLocalPlayerBuild()
-- returns raw build (table) of local player
Build.GetPackedLocalPlayerBuild()
-- returns packed build (string) of local player
Build.PackBuild(build)
-- returns packed build
Build.UnpackBuild(packedBuild)
-- returns unpacked build (table)Build itself is a table with schema defined in extra/build/main.lua. It consists 16 parts:
local ALLIANCE = 1
local AVA_RANK = 2
local RACE = 3
local CLASS = 4
local LEVEL = 5
local CP = 6
local SKILLS = 7
local FIRST_BOON = 8
local SECOND_BOON = 9
local WW_VAMP_BUFF = 10
local ATTRIBUTES = 11
local STATS = 12
local GEAR = 13
local CONSTELLATIONS = 14
local FOOD = 15
local CLASS_SKILL_LINES = 16You can acces any part with these enums like this:
local build = Build.GetLocalPlayerBuild()
local allianceId = build[Build.ALLIANCE]
-- returns player allianceId
local food = build[Build.FOOD]
-- returns food buff idPlease refer to extra/build/main.lua to see how fields defined. Most of them are plain numbers:
- ALLIANCE
- AVA_RANK
- RACE
- CLASS
- LEVEL
- CP
- FIRST_BOON
- SECOND_BOON
- WW_VAMP_BUFF
- FOOD
or tables/arrays. Some of them:
Item(table)
local Item = Field.Table('item', {
Field.Number('id', 20),
Field.Number('quality', 3),
Field.Number('trait', 6),
Field.Number('enchantmentId', 20),
}, IGNORE_NAMES)
-- for example
local item = {
123456, -- id
7, -- quality
8, -- trait
901234 -- encahnmentId
}Gear(array)
Field.Array('gear', 14, Item)Gear is an array of 14 items. To get particular piece, you can use default EQUIP_SLOT_... globals:
-- EQUIP_SLOT_HEAD
-- EQUIP_SLOT_CHEST
-- EQUIP_SLOT_SHOULDERS
-- EQUIP_SLOT_HAND
-- EQUIP_SLOT_WAIST
-- EQUIP_SLOT_LEGS
-- EQUIP_SLOT_FEET
-- EQUIP_SLOT_NECK
-- EQUIP_SLOT_RING1
-- EQUIP_SLOT_RING2
-- EQUIP_SLOT_MAIN_HAND
-- EQUIP_SLOT_OFF_HAND
-- EQUIP_SLOT_BACKUP_MAIN
-- EQUIP_SLOT_BACKUP_OFF
build = GetLocalPlayerBuild()
local chest = build[Build.GEAR][EQUIP_SLOT_CHEST]
local head = build[Build.GEAR][EQUIP_SLOT_HEAD]
-- ... your code here
-- or with iteration
for equipSlot = EQUIP_SLOT_ITERATION_BEGIN, EQUIP_SLOT_ITERATION_END do
local piece = build[Build.GEAR][equipSlot]
-- ... your code here
end-- Hotbar slot type
build:GetSlotType(hotbarCategory)- hotbarCategory: HOTBAR_CATEGORY_PRIMARY or HOTBAR_CATEGORY_BACKUP
- returns: slot type (ACTION_TYPE_ABILITY or ACTION_TYPE_CRAFTED_ABILITY)
-- Hotbar slot bound id
build:GetSlotBoundId(slotIndex, hotbarCategory)- slotIndex: slot index, 3-8
- hotbarCategory: HOTBAR_CATEGORY_PRIMARY or HOTBAR_CATEGORY_BACKUP
- returns: slot bound id
-- Hotbar slot script ids
build:GetSlotScriptIds(slotIndex, hotbarCategory)- slotIndex: slot index, 3-8
- hotbarCategory: HOTBAR_CATEGORY_PRIMARY or HOTBAR_CATEGORY_BACKUP
- returns: slot script ids
-- Player stat
build:GetPlayerStat(statId)- statId: stat id, e.x. STAT_HEALTH_MAX, STAT_PHYSICAL_PENETRATION, etc.
- returns: stat value
These functions are close to ZOs default functions, so you can easily replace them:
-- BEFORE
local health = GetPlayerStat(STAT_HEALTH_MAX)
local magicka = GetPlayerStat(STAT_MAGICKA_MAX)
-- AFTER
-- in header
local Build = LibImplex.Extra.Build
-- get local player's build
local build = Build.GetLocalPlayerBuild()
-- or unpack some other build
local build = Build.UnpackBuild(...)
-- get stats from this build
local health = build:GetPlayerStat(STAT_HEALTH_MAX)
local magicka = build:GetPlayerStat(STAT_MAGICKA_MAX)You can also refer to extra/build/ui.lua for more examples how to use each part of build. Layout... functions will be a good demonstration how to get and use one part or another:
LayoutBasicInfo(build) -- level, CP, race, etc.
LayoutGear(build) -- gear
LayoutSkills(build) -- skills
LayoutAttributes(build) -- max HP, mana, stam
LayoutStats(build) -- resists, penetration, crit, wpd/spd
LayoutConstellations(build) -- constellations
LayoutSkillLines(build) -- skill lines (for subclassing)
LayoutBoons(build) -- boons
LayoutFood(build) -- food buff
LayoutVampireOrWWBuff(build) -- vampire (ww) buff