Self-hosted service
opentibiabr/otclient avatar
opentibiabr/otclient

OTClient is a Tibia client where the whole interface is Lua and the engine is C++20

An alternative tibia client for otserv written in C++20 and Lua, made with a modular system that uses lua scripts for ingame interface and functionality, making otclient flexible and easy to customize

540 stars548 forksLuaNOASSERTION

At a glance

What is it?
OTClient Redemption is an alternative client for the OTServ family of Tibia servers, written in C++20 and heavily scripted in Lua, with each piece of game functionality living in its own module. It targets the Open Tibia protocol through TFS and Canary servers, and the upstream reference is edubart/otclient at revision 2.760.
Who is it for?
Adopt OTClient if you run an OTServ compatible server and want to give your players a client you control, because the interface, the modules and the server protocol are all open and the Lua layer makes feature work a scripting job. Do not adopt it if you need an official or mainstream client, because this is an alternative client for the Open Tibia ecosystem and speaks that protocol only.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Lua, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What it is: an alternative client for the Open Tibia server family

OTClient is an alternative Tibia client for usage with OTServ, and it aims to be complete and flexible. The README gives five properties in a list, and each one is a design decision rather than a feature bullet.

Lua scripting covers all game interface functionality. CSS-like syntax handles UI design. A modular system puts each functionality in a separate module so it can be customised easily, and users can create new mods and extend the interface. The code itself is written in C++20 and heavily scripted in Lua.

The server side is named explicitly, and that is the constraint that shapes everything else. For a server to connect to, you can build your own with theforgottenserver or canary. So this is not a client for CipSoft's official Tibia, it is a client for the open server implementation, which means the protocol it speaks is the Open Tibia one rather than the proprietary protocol.

The lineage is also stated. The project is based on edubart/otclient at revision 2.760, and the README's header names it OTClient - Redemption, while the repository metadata describes it as an alternative tibia client for otserv written in C++20 and Lua, made with a modular system that uses lua scripts for ingame interface and functionality.

So there are three layers to keep straight. The C++20 engine handles rendering, input and networking. Lua implements the interface on top of it. The protocol is Open Tibia's, shared with TFS and Canary servers.

The repository layout reflects that division. There is src/ for the C++ engine, modules/ for the Lua game modules, mods/ for user modifications, data/ for assets, and the entry points init.lua and meta.lua at the root alongside otclientrc.lua and config.ini.

The module system is the extension point, and it is what the design is for

The modular system is listed as a headline property, and the README's later sections show what that means in practice.

Each functionality is a separate module, and the Module Controller System is described as a safer way to create modules, without the need to unbind keys, disconnect events, or destroy widgets. There is a worked example in modules/game_minimap/minimap.lua.

That description is the real content of the design. In a scripting-heavy client, the classic failure mode is a module that installs a key binding or subscribes to an event and then tears itself down without undoing either, leaving a dead key or a dangling callback that fires into freed state. A controller that owns the lifecycle removes the need for every module author to remember the cleanup, and that is the difference between a modding API people can use and one they avoid.

The Auto Reload Module is the other piece of the story, and it is enabled with g_modules.enableAutoReload(), called from init.lua. That is a workflow feature rather than a game feature, and it exists because the Lua layer is meant to be edited constantly: you change a script, the client reloads it, you do not rebuild.

The Attached Effects System shows how far the module approach reaches. It handles auras and wings, is compatible with .APNG, and covers three separate things: ThingCategoryEffect, ThingCategoryCreature, and ThingExternalTexture, with images in PNG or APNG. There is a wiki tutorial, example code in modules/game_attachedeffects/effects.lua, and a per-lookType configuration example in configs/outfit_618.lua. The README adds a tip that you can adjust offsets per lookType using ThingConfig when a default offset does not align perfectly for a given sprite.

That last point is worth noting for anyone planning custom work. Attaching an effect to a creature is not just picking a texture; the sprite has to line up with the base animation, which means a per-outfit configuration file. The API is expressive enough to expose that as a setting rather than baking offsets into the artwork.

The engine work: threading, garbage collection and an OpenGL 2.0 ES base

The Performance and Engine section is where the C++20 shows up, and it is organised as a set of expandable detail blocks rather than prose.

Multi-threading is described by splitting work across three threads with an explicit division of labour. The main thread handles sound, particles, loading textures from files, window events for keyboard and mouse, and drawing textures. A second thread handles the connection, events dispatched through g_dispatcher, and collecting information about what will be drawn on the map. A third thread collects information about what will be drawn in the interface.

The separation of UI collection from map collection is the interesting part. The interface can change constantly while the map underneath is static, and putting the two collection passes on separate threads is what keeps a redraw from serialising into a single frame.

The Garbage Collection feature is described in two ways. The first is the general explanation: the feature is responsible for automatically managing memory by identifying and releasing objects that are no longer in use, which allows the client to maintain efficient memory usage, avoid unnecessary data accumulation and improve overall stability. The second names what it actually reclaims: lua, texture, drawpool and thingtype objects.

Asynchronous Texture Loading is the third block, and its description is precise about the trade-off: with it the spr file is not cached, consequently less RAM is consumed. That is the classic texture-pack problem in a game client. The sprite sheet is large, caching it makes lookups fast, and not caching it saves memory at the cost of re-reading. The README presents the option rather than a default.

Underneath, the engine is an OpenGL 2.0 ES engine, which the feature list names as the thing that allows porting to mobile platforms. The build targets C++20 with vcpkg manifest mode, in x32 and x64, and the feature list also records support for sequenced packages and compression, walking system improvements, and asserts load for Tibia 13.

The Lua layer is where the UI, the tools and the terminal live

Beyond the modules, the README describes OTClient as flexible enough to create Tibia tools like map editors using scripts, and calls it a framework plus Tibia APIs. That is a different claim from being a game client, and it is the one that makes the project interesting to use.

The UI itself uses CSS-like syntax, so styling is declarative rather than a set of draw calls in Lua. The feature list calls out a styleable UI with transparency and multi-language support, along with a sound system, graphics effects with shaders, animated textures, modules and addons, and an in-game Lua terminal.

The in-game Lua terminal deserves a note, because it changes what a server operator can do. With a scripting console in the client, the debugging loop is immediate: you evaluate expressions against the live game state, inspect what the server sent, and test a change without rebuilding anything. The GUI improvements section also mentions improvements in the UI algorithm with better performance in add, remove and reposition widgets, visible in the battle module.

Anti-Aliasing Mode Options are also listed, with the note that Smooth Retro will consume a little more GPU. The presence of a retro-style mode tells you the target audience includes people who want the old pixel look, which is the same audience that wants to run their own server.

The Lua API surface is meant to be the same as the client API, and the fact that the engine is C++20 while the scripting is Lua means a mod author writes Lua and never touches C++. That is the whole argument for the architecture: the parts that need performance are in the engine, and the parts that change per server are in scripts.

Building it: CMake, vcpkg and a Docker path

The repository layout shows a modern build system. There is a CMakeLists.txt at the root, a CMakePresets.json, a vcpkg.json with a vcpkg-configuration.json, a cmake/ directory, and a Dockerfile alongside Dockerfile.android, Dockerfile.browser and Dockerfile.browser.sh. The presence of a browser Dockerfile is the OpenGL 2.0 ES choice showing up in practice.

The Dockerfile is the clearest statement of the dependency set, and it starts from ubuntu:24.04 with a dependencies stage. The header lines are:

dockerfile
# syntax=docker/dockerfile:1.7

FROM ubuntu:24.04 AS dependencies

ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=Etc/UTC
ARG VCPKG_FEED_URL
ARG VCPKG_FEED_USERNAME
ARG VCPKG_BINARY_CACHE_ACCESS=read
ARG VCPKG_BINARY_SOURCES
ENV VCPKG_BINARY_SOURCES=${VCPKG_BINARY_SOURCES}

The build then installs the toolchain and the graphics libraries through apt with a cache mount, including autoconf, automake, build-essential, cmake, jq, the Mesa and GL development packages, libltdl-dev, libtool, the X11 development headers for input and display, ninja-build, pkg-config and python3.

Then the vcpkg step, and this part is the interesting one for anyone reproducing a build:

dockerfile
WORKDIR /opt
COPY vcpkg.json /opt/vcpkg.json
RUN vcpkgCommitId="$(jq -r '."builtin-baseline"' vcpkg.json)" \
	&& echo "vcpkg commit ID: ${vcpkgCommitId}" \
	&& git clone https://github.com/microsoft/vcpkg.git \
	&& cd vcpkg \
	&& git checkout "${vcpkgCommitId}" \
	&& ./bootstrap-vcpkg.sh

The vcpkg commit is read out of the manifest's builtin-baseline with jq rather than being pinned in the Dockerfile, so the manifest is the single source of truth for dependency versions. The overlay-ports directory is copied separately afterwards, with a comment that it keeps the manifest's overlay port available to both published and CI-only Docker builds, and the vcpkg binary cache is configured with read access by default, which means a build can consume a cache it cannot poison.

The binary cache arguments are the other part of that setup, since VCPKG_BINARY_SOURCES and VCPKG_BINARY_CACHE_ACCESS are passed in as build arguments rather than hardcoded. That is what lets an organisation point builds at an internal binary cache instead of rebuilding every dependency from source.

The repository also carries platform-specific helper scripts: build_luajit_android.sh, setup_android_deps.sh, setup_android_wsl.sh, recompile.sh, plus the vc18/ directory and the android/ and browser/ directories.

Where OTClient is the wrong choice

The first limitation is the protocol, and it is not negotiable. This client speaks the Open Tibia protocol to TFS or Canary servers, so if your server speaks the official Tibia protocol, or you want your players to use the official client, OTClient is not an option. The README is explicit that a server to connect to is one you build with theforgottenserver or canary.

The second limitation is that it is a client you now operate. The build needs a C++ toolchain, vcpkg, the OpenGL and X11 development headers, and LuaJIT for the scripting layer. The Android path has its own dependency scripts and a separate Dockerfile, and the browser path has another. None of that is a one command install for a player, which means every distribution channel you want, from a launcher to a Steam entry, becomes your problem.

The third limitation is the base. This project is based on edubart/otclient at a specific revision, 2.760, and it is a fork with its own identity. That means upstream bug fixes and protocol updates do not arrive automatically, and the work of merging them is the fork's maintainers' job. The recent release history shows that work happening, with 4.0 and 4.0b4 and 4.1 across the last year, so the fork is active, but it is still a fork with a pinned upstream base.

The fourth limitation is the dependency of the whole approach on the Lua layer being the intended extension point. That is a strength for a server operator who wants to add features, and a weakness for a player who just wants a client that works, because the client you distribute has to be built and tested against the server you run.

The alternative to weigh is the other clients in the same ecosystem, and the difference in approach is about where you sit. The original edubart/otclient is the upstream that this fork is based on, and it shares the same Lua architecture but without this fork's engine work on threading, garbage collection and the attached effects system. A server operator who wants stability and a large existing base of third-party scripts takes the upstream; an operator who wants the newer engine and the module controller takes this fork. Either way the choice of protocol is the same constraint, and that is the one that decides whether the question matters.

Licence, moderation and the shape of the repository

There is a licence discrepancy worth flagging, because it is the kind of thing that matters before you redistribute a client.

The repository listing classifies the licence as a custom one that GitHub cannot classify, with a note to see the LICENSE file. The README's badge, however, reads License: MIT and links to the MIT text on opensource.org, and the table of contents has a License section. The top level entries include a LICENSE file, an AUTHORS file and a CHANGELOG.md, so the terms are in the repository, but the metadata and the badge disagree about how the licence is categorised. Reading the LICENSE file directly is the step to take rather than relying on either label.

The repository also carries the pieces a project of this kind needs. BUGS.md sits next to CONTRIBUTING.md-style files and a .reviewdog.yml for automated review, with a .yamllint.yaml and an .editorconfig and a .gitattributes for line endings. There is an AGENTS.md at the root, which suggests the project has been set up for agent-assisted development. tests/ and tools/ directories exist alongside src/ and modules/.

The moderation and support structure is documented in the table of contents. There is a Bugs section, a Need Help? section, a Roadmap section, a Support Protocol section and a Contributors section, plus a Discord badge pointing at the project community. There is also a Client Assets Auto-Install document in docs/, which is a separate topic in the table of contents, so client data distribution is treated as its own problem rather than an afterthought.

On maintenance, the last push was on 2026-10-17 and the repository is not archived. The release history shows 4.1 in August 2026, 4.0 with a hotfix in January 2026, and 4.0 Beta 4 in August 2025, which is a normal cadence for a project whose major version has been settling since the 4.0 line.

The primary language in the repository metadata is Lua, which is the right signal for a project whose selling point is that the interface and features are scripts. The C++20 engine is the part that stays stable while the Lua changes, and that separation is the design.

Editorial conclusion

Adopt OTClient if you run an OTServ compatible server and want to give your players a client you control, because the interface, the modules and the server protocol are all open and the Lua layer makes feature work a scripting job. Do not adopt it if you need an official or mainstream client, because this is an alternative client for the Open Tibia ecosystem and speaks that protocol only. Verify first which server your build targets, since TFS and Canary differ, that the licence file matches what the README badge claims, and that the module and mod directories in the repository cover the features you actually need to customise.

Frequently asked questions

What is OTClient and which servers does it connect to?

OTClient is an alternative Tibia client for usage with OTServ, written in C++20 and heavily scripted in Lua. The README says that for a server to connect to, you can build your own with theforgottenserver or canary, so it targets the Open Tibia server family rather than the official game client.

What is the module controller system in OTClient?

It is described as a safer way to create modules, without the need to unbind keys, disconnect events, or destroy widgets, with modules/game_minimap/minimap.lua given as the worked example. Each functionality is a separate module, and users can create new mods and extend the interface.

How does OTClient handle multi-threading?

Work is split across three threads. The main thread handles sound, particles, loading textures from files, window events and drawing, the second handles the connection, events through g_dispatcher and collecting what will be drawn on the map, and the third collects what will be drawn in the interface.

How do I build OTClient?

The repository uses CMake with a CMakePresets.json and vcpkg manifest mode, and the Dockerfile shows the dependency set built on ubuntu:24.04 with apt packages for the toolchain and graphics headers, then vcpkg checked out at the builtin-baseline commit read from vcpkg.json with jq. Platform-specific setup scripts exist for Android and the browser.

What licence is OTClient released under?

The repository listing classifies it as a custom licence that GitHub cannot classify, pointing to the LICENSE file, while the README badge reads License: MIT and the top level entries include a LICENSE file, an AUTHORS file and a CHANGELOG.md. The last push was on 2026-10-17 and the repository is not archived, with releases 4.1, 4.0 and 4.0 Beta 4 in the recent history.

Official sources

  1. Issues
  2. opentibiabr/otclient on GitHub
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/opentibiabr-otclient.svg)](https://hysenlabs.com/projects/opentibiabr-otclient)

Was this useful?