Skip to content

feat: multi spr file support#1552

Draft
Zbizu wants to merge 51 commits intoopentibiabr:mainfrom
Zbizu:resources
Draft

feat: multi spr file support#1552
Zbizu wants to merge 51 commits intoopentibiabr:mainfrom
Zbizu:resources

Conversation

@Zbizu
Copy link
Contributor

@Zbizu Zbizu commented Jan 7, 2026

Description

I am looking for testers and collaborators on this.

This change is a HUGE BREAKTHROUGH for servers that want to have both custom sprites and use most recent assets.
It allows loading multiple spr/dat/assets SIMULTANEOUSLY (at the same time) which means that it will be possible to USE MULTIPLE SPRITE PACKS

Everyone will benefit, we can start a new chapter in the history of OpenTibia
I already have server side and mapeditor loader for this tech, and half-finished code for a format that will replace otbm and support these changes. This means that this technology is no longer a dream, it's something reachable, possibly this year.

Here is how it'll work:

  • one directory will hold vanilla assets (updating will be just drag and drop)
  • the other directory will hold your custom assets (or spr/dat)
  • things will be accessed by two coordinates now: clientId, resourceId

This does two things:

  • bypasses u16 item id limit (which vanilla client will likely do anyway, but it might be up to 10 years away looking at the current amount of items)
  • solves all problems with managing custom sprites in a project that is constantly updating vanilla sprites

This PR is highly experimental, unexpected things may happen while testing.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested

It wasn't tested properly yet.
So far I managed to log into game with a two asset packs loaded and change outfit client-side with:
g_game.getLocalPlayer():setOutfit({type = 1921, resourceId = 1})

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I checked the PR checks reports
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works

@Zbizu
Copy link
Contributor Author

Zbizu commented Jan 7, 2026

demo:

2026-01-07.14-51-49.mp4

@InnerCircleTFS
Copy link
Contributor

InnerCircleTFS commented Jan 7, 2026

legacy

I don't like being forced to have an OTFI.

1098

Loading game assets from /data/things/1098/
Loading resource 0 (spr/dat) from /data/things/1098/ ...
ERROR: Failed to read dat '/data/things/1098/Tibia.dat': Failed to format date-time string
ERROR: Failed to read Tibia.dat: file structure does not match the defined version or OTFI specification

Even with an OTFI, an error occurs.

DatSpr
  extended: true
  transparency: false
  frame-durations: true
  frame-groups: true
  metadata-file: Tibia.dat
  sprites-file: Tibia.spr
  sprite-size: 32
  sprite-data-size: 4096

13.10 with GameLoadSprInsteadProtobuf

i add g_game.enableFeature(GameLoadSprInsteadProtobuf)
to explicitly load spr and not assets

Some believe that the assets system is very poorly optimized and prefer .spr/.dat with GameLoadSprInsteadProtobuf

but

Loading game assets from /data/things/1310/
Loading resource 0 (assets) ...
WARNING: Failed to load '/sounds/1310/' (Sounds): unable to open file '/sounds/1310/catalog-sound.json': not found
Assets loading complete.

but I don't load anything, neither the assets nor the .spr/.dat

13.10 with assets

game_actionbar

https://github.com/mehah/otclient/blob/6ed3b05311671690a72f8a931af23ac0fbef6330/modules/game_actionbar/logics/ActionButtonLogic.lua#L685

https://github.com/mehah/otclient/blob/6ed3b05311671690a72f8a931af23ac0fbef6330/modules/game_actionbar/logics/ActionButtonLogic.lua#L707

button.item:setItemId(useAction, true) -> button.item:setItemId(useAction)

by the way In UIItem, how would you put resourceId?

@Zbizu
Copy link
Contributor Author

Zbizu commented Jan 7, 2026

@InnerCircleTFS I only tested on assets, thank you for your feedback

by the way In UIItem, how would you put resourceId?

below item-id just add item-resource-id with resource number

@InnerCircleTFS
Copy link
Contributor

InnerCircleTFS commented Jan 7, 2026

I was wondering about that. Because you forgot to add it.

image

but you added it to
Uicreature, uieffect,uimissile , uisprite, but not to UIitem

image

@Zbizu
Copy link
Contributor Author

Zbizu commented Jan 7, 2026

roadmap:

  • better handling of legacy sprites (assuming proper version-specific structures when no otfi is provided)
  • notification in terminal when multiple sprites are loaded, but protocol feature is not enabled
  • documentation for resource packs
  • removing deprecated feature check from async texture loader
  • refactor init/terminate for resource manager
  • test with aura/wings/effects/shader
  • resourceId in send methods
  • look into onLoadDat behaviour (currently likely to be called multiple times)
  • simplify access to sprite managers
  • check if the garbage collector works properly
  • check if the code doesn't leak memory
  • address merge conflicts
  • fix build on framework_editor

@Zbizu
Copy link
Contributor Author

Zbizu commented Jan 8, 2026

@InnerCircleTFS I addressed reported issues
im going to address some sonar issues tomorrow

Instructions for testers

in data/things/(version) create packinfo.xml:

<?xml version="1.0" encoding="UTF-8"?>
<resources>
	<resource id="0" version="1320" dir="normal"/>
	<resource id="1" version="1098" dir="custom"/>
</resources>

drop your sprites (or assets) to in data/things/(version)/normal/ and in data/things/(version)/custom/

set the version to match the resource (eg. if your custom dat is for version 10.98, set it to version 1098)

Known issues

  • drawpool crashes after 10 minutes (not related to this pr, for a solution see Crash: drawPool #1555)
  • (not a bug) having some resources as spr/dat and others as assets is okay, mind that otc may fail to read item classifications from legacy spr/dat resource, thus causing protocol mismatch.

Advanced users

to test protocol changes (read only for now, no send methods yet), uncomment g_game.enableFeature(GameMultiSpr) in things.lua

@Zbizu
Copy link
Contributor Author

Zbizu commented Jan 10, 2026

to do:

  1. auras/wings/etc support
  2. protocol send methods

@javiertringol
Copy link
Contributor

old protocol

ERROR: Lua exception: /gamelib/protocollogin.lua:54: attempt to index global 'g_sprites' (a nil value)
stack traceback:
    [C]: in function '__index'
    /gamelib/protocollogin.lua:54: in function 'connectCallback'
    /gamelib/protocollogin.lua:152: in function </gamelib/protocollogin.lua:150>
ERROR: protected lua call failed: LUA ERROR:
/gamelib/protocollogin.lua:54: attempt to index global 'g_sprites' (a nil value)
stack traceback:
    [C]: in function '__index'
    /gamelib/protocollogin.lua:54: in function 'connectCallback'
- g_lua.bindSingletonFunction("g_sprites", "getSprSignature", &SpriteManager::getSignature, &g_sprites);
function ProtocolLogin:sendLoginPacket()
    local msg = OutputMessage.create()
    msg:addU8(ClientOpcodes.ClientEnterAccount)
    msg:addU16(g_game.getOs())

    msg:addU16(g_game.getProtocolVersion())

    if g_game.getFeature(GameClientVersion) then
        msg:addU32(g_game.getClientVersion())
    end

    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()) --<---------HERE

open game_outfit

ERROR: Creature::setOutfit - Invalid thing type for creature 0.

game_actionbar
ERROR: Lua exception: /game_actionbar/logics/ActionButtonLogic.lua:478: attempt to index a nil value

@Zbizu
Copy link
Contributor Author

Zbizu commented Jan 11, 2026

@javiertringol
Thanks, fixed.
The action bar bug was already fixed in previous commits.

@Zbizu
Copy link
Contributor Author

Zbizu commented Jan 11, 2026

new: added render code for auras and wings
new: added mount colors (not tested with outfit window yet)
new: auras and wings now support resource id

2026-01-11.05-36-43.mp4

@javiertringol
Copy link
Contributor

The action bar bug was already fixed in previous commits.

Are you sure? the error is different
main:
image

your video:
image

@sonarqubecloud
Copy link

@Zbizu
Copy link
Contributor Author

Zbizu commented Jan 12, 2026

the packets are pretty much ready, but the modules will require some extra work

Now I intend to modify some server to be compatible with these changes.
I intend to target higher protocols, therefore the most suitable candidate for me personally is the Canary server.

When this gets merged: opentibiabr/canary#3794
I should be able to bump both otc and the server itself to protocol 15.20

@javiertringol bug with items in action bar is now fixed

@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

This PR is stale because it has been open 45 days with no activity.

@github-actions github-actions bot added the Stale label Feb 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants