Permissions > Use Privilege Key redeems a key (tokenuse). Permissions >
Privilege Keys opens a per-tab window listing the server's keys with
their group, channel, creation date and description; keys can be created,
removed, copied and reloaded. The create dialog takes a server group, or a
channel group in a chosen channel, plus a description, offers only groups
we have the power to add members to, and shows each new key for copying.
tokenlist and tokenadd answer with notifytokenlist / notifytokenadd ahead
of the command's completion, so core collects them the way it does the
ban list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client sheet offers kicking from the channel or the server (asking to
confirm, with an optional reason), banning with a reason and duration, and
the client's server groups and channel group. The server sheet opens the ban
list: search, own bans only, and adding, editing and deleting bans, with the
reason presets the desktop keeps.
Kick and ban powers, like move power, never come with the needed
permissions, so core now asks for all four in one piped permget. The ban
permission checks, a ban's summary, the duration units, the group choices
for a client and the kick reason limit move into core, and Swing uses them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It only repeated the page selected in the sidebar, and took vertical
space from the page itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A sidebar of pages with large icons replaces the tabs, and each page
opens under a banner with its title, subtitle and icon. OK, Cancel and
Apply sit below, and the dialog reopens on the last page used.
The pages follow TS3's: Application (muted-microphone warning, reported
client version), myTeamSpeak, Playback, Capture, Design, Hotkeys,
Contacts, Chat and Notifications. Capture now holds everything from the
microphone to the server: the device and gain, activation with the
microphone test, signal processing and the encoder, laid out in one
grid so its columns line up. Forms are grouped under section headings.
Cancel now goes back to what was last applied, and closing the window
cancels too, so a theme or icon pack being tried out is put back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Text and pictures in the chat tabs and the info panel zoom in browser
steps (50-300 %), keeping the line at the top in view. The chat and the
info panel each have their own level, shared by every server tab and
saved in the settings.
The HTML documents now take their fonts from a stylesheet that applies
the zoom. The old ScaledStyleSheet never reached the document, and
FlatLaf already applies the interface scale, so it only zooms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The strip is out of the component tree while one server is open, so a
look-and-feel switch missed it and it came back in the old theme's colours.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With b_client_avatar_delete_other, the client's context menu and the
menu on their avatar offer "Delete Avatar", as in TeamSpeak 3. The
server takes the deletion as ftdeletefile on /avatar_<unique id> (not
the stored file's name, which it refuses with "convert error") and
clears the client's avatar flag for everyone. Our own avatar's file is
now deleted the same way, which only such clients are allowed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As in TeamSpeak 3, Self offers "Set avatar…", which picks an image and
uploads it, and "Delete avatar" while we have one; the old avatar
dialog is gone. Setting (and stealing) is offered only while the server
grants i_client_max_avatar_filesize. Right-clicking our own avatar in
the info panel offers the same two beside saving it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Right-clicking a client's avatar offers to save it to a file or to take
it as our own avatar on that server, as the Android picture viewer does.
Our own avatar only offers saving.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A find button in the top bar opens a find bar over the tree (clients or
channels) or the conversation on screen, with match stepping, highlights,
only-matches filtering and the desktop's matching options. The tree's
match rule moves to core TreeMatch, which the desktop now uses too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server places each client in a country (client_country, from its GeoIP
database); a client's details now show that country's flag left of their
name, as TeamSpeak's info frame does, on both clients. "Show country flags
in the channel tree" (off by default, as TeamSpeak's EnableCountryFlags)
also puts it last on client rows.
The flags are TeamSpeak's countries.zip, which now ships with the icon
packs; core's CountryFlags reads it, else a TeamSpeak 3 install's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[img] in a channel, client or server description is now a picture on both
clients, as TeamSpeak shows it; chat keeps it a link. Only web addresses
become pictures. While one loads it is TeamSpeak's loading icon; one that
cannot be had is its address behind the broken-picture icon.
"Show pictures in descriptions" (desktop: Chat settings; Android: Appearance)
turns them into links behind a picture icon instead, fetching nothing. A
picture that was itself a link keeps that link beside it.
Core's PictureDownloads fetches them for both clients: kept on disk for a
month after last shown, at most 8 MB, redirects followed across http and
https, and on Android plain http asked for over https. The desktop draws
them narrowed to the info panel, Android at a pixel to a dp, animated GIFs
playing on both; long addresses are shortened to host and file name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core's Details says what the info box tells, as TeamSpeak's info frame does:
labelled facts, then a BBCode description. The desktop info panel now lays it
out instead of building its own, and selecting the server row shows the
server: address, version, clients, channels, creation date and its welcome
and host messages, from the virtualserver_* properties kept since initserver.
On Android the channel and client sheets show the same details, asking the
server for a missing channel description or a client's variables; tapping
the server row opens a server sheet. Descriptions render as TeamSpeak's do:
BBCode now knows [hr], [left]/[right]/[justify], numbered and lettered lists
and tables, which Android's HTML parser approximates — rules as dividers,
table rows as lines, list items numbered, font sizes as big/small. Links are
blue whatever the wallpaper.
Push-to-talk in the bottom bar follows whichever server holds the microphone,
and a server without it keeps its button to take it in a fixed slot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A contact on two servers at once was marked seen by each in turn: a sighting
announced a contact change, every connection re-applied its contacts, and
each re-application recorded the contact on its own server — another change,
round and round, rewriting contacts.txt every time. Re-applying now only marks
a contact that was never seen; the last sighting moves when the client appears.
Sightings no longer write the file on their own either: they reach it in one
save half a minute later, or with the next edit's save, and are flushed when
the client closes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings -> Identities lists them and creates, imports (an identity file or a
TeamSpeak 3 settings.db), exports, renames and removes them, picks the
default and improves an identity's security level; the search runs on until
reached or stopped, whichever page is on screen. The Connect screen and each
bookmark choose the identity to connect with.
An identity keeps a nickname and phonetic nickname, in TeamSpeak's own INI
keys, on both clients. A connect goes by the bookmark's nickname, else the
identity's, else the global one; the phonetic one is sent in clientinit.
Imported bookmarks no longer copy their identity's nickname.
IdentityStore is now safe to share with the thread a connect resolves its
identity on, and imports and exports through streams for Android's document
picker.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While none of the client's windows has the focus, a poke or a private
message shows a desktop notification, which the desktop's notification
centre keeps. A private conversation keeps one notification, updated as
messages come; clicking it brings the window forward with that chat open,
and they all go once a window of the client is in front again.
On Linux they go to the freedesktop.org notification service, spoken to
over the session bus by a small D-Bus client of our own on Java's Unix
sockets, so no library or native code is involved; Plasma also takes a
reply typed into the notification. Elsewhere they are AWT tray messages,
which Windows and macOS put in their notification centres. The options
can turn them off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Docking waits for the panel to adopt the icon, which held up the main
window; wait at most half a second for it before falling back to AWT.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each contact shows just the date it was last seen, without the client
dot and at half the usual nesting; the full time and server move under
the phonetic nickname as "Last seen … on …".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The contacts window drops its tabs for one column of short groups beside
the list: contact type, display and ignore, where whisper becomes a
"Deny whisper" box, since allowing is the default. The names get their
labels on a line of their own, and the window is never shorter than the
options, leaving the list the only thing that scrolls.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Android app's Material icons become a TeamSpeak-format pack shipped in
core, which the desktop offers next to the TeamSpeak packs. Android now
draws its icons from packs too and imports TeamSpeak's own zips, falling
back to the Material pack for what they lack.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The myTeamSpeak client protocol and its end-to-end encryption are
reverse-engineered (docs/myteamspeak): an HTTP POST of a framed protobuf,
a PBKDF2-HMAC-SHA512 login token and account key, an AES-GCM wrapped
item key and AES-CTR items with a SHA-512 trailer.
Options on desktop and Settings on Android gain a myTeamSpeak page: sign
in and out, and the account's bookmarks and identities, each picked for
import and marked when already here. The client stays signed in the way
the official one does, keeping the derived token and key rather than the
password; each read signs in afresh, and a sign-in the server no longer
takes signs out. Nothing is written to the account.
Items are the same Item_Data as the local TeamSpeak store, so they go
through the existing decoder and importer; a bookmark chosen alone brings
the identity it connects with, found by UUID or, as some name it, by
the identity's name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGC2 won't amplify the background noise above -50 dBFS, which also caps
the gain for a voice barely above that noise: a distant talker ended up
5 dB quieter than a near one with noise suppression on, 12 dB with it
off. A "Boost quiet speech" setting (0-20 dB, default 0) raises the cap,
live, on desktop and Android.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Android client needs them too: which state a client's icon shows,
how spacer channels are named, and the HTML a chat line is written as.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Which server captures with the microphone, handing it over, push-to-talk
and key presses reaching the right microphones, and away state and
nickname applied everywhere were all worked out in MainFrame. They move
to a Sessions class in core, next to ServerSession, where another
frontend can use them; the window keeps only the tabs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every tree rebuild, which any join, leave or talk-power change causes,
expanded every row again, so a collapsed channel reopened within
seconds. The tree now remembers which channels were collapsed and
opens everything else; reopening one restores what is below it. While
the find bar filters, everything opens so every match shows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chat logs always went into the TeamSpeak 3 client's own folder. A new
Chat page in Options can turn them off, or point them at another
folder; left empty, they are still shared with the TeamSpeak 3 client.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each hand-over ran on a fresh thread, so two quick tab switches could
start the new tab's capture before the old one stopped, or stop it after
it started. They now run one after another on a single thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hotkeys.all() returned its synchronised list, and every reader had to
lock it while iterating: a lock owned by one class and taken by four
others. It now returns a copy, and the lock stays inside Hotkeys.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Swing client menu decided on its own whether a group can be
(un)assigned and whether a client's channel can be joined, and the
server groups dialog borrowed the menu's check. These are rules about
the server, not about menus, so ServerModel answers them now and any
frontend asks it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What a server tab knows beyond its views - the connection lifecycle,
title and status, the identity used, the microphone/speaker/away and
commander flags, chat sending and the channel lookups - moves from the
Swing ServerTab and ServerTabSelfState into core's ServerSession, so
another frontend gets the same behaviour without copying it.
ServerSession also takes over what the Swing event bridge decided on its
own: clearing the model on disconnect, naming the tab after the server,
showing senders under their contact names, and the chat's system lines.
The bridge now only updates views. SelfState moves along, while its
icons stay with the tray.
A server switch no longer needs to queue behind the UI thread: the
disconnect is reported synchronously, so the session is free again by
the time it reconnects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Capture processing, the jitter buffer and decoding were desktop classes,
so any other platform would have had to copy them. They now live in core
and reach the platform only through two interfaces:
- AudioIo lists devices and opens capture/playback lines; the desktop's
AudioDevices implements it over PipeWire and Java Sound.
- OpusCodec creates encoders and decoders; the desktop binds libopus
through the FFM API as before.
DesktopVoiceInput becomes CaptureVoiceInput unchanged in behaviour.
DesktopVoiceOutput splits into VoiceStream, one speaker's jitter buffer
and decoder, paced by whoever pulls it, and StreamingVoiceOutput around
it. Speakers reach the device either on a line each (the desktop, so
each shows up in the PipeWire mixer) or mixed onto one shared line, as
mobile audio APIs want.
The settings dialog's device lists and microphone test now go through
the AudioBackend instead of desktop classes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stores read their paths from AppDirs instead of user.home, so a platform
without a home directory can point them elsewhere at startup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The capture path now runs the ported WebRTC chain in place of the
home-made noise, typing and gain stages, which are removed.
- As in TS3, the speech detector judges the raw microphone signal, while
the level meter and volume gate see the processed one.
- Noise removal takes TS3's four levels (6, 12, 18 or 21 dB); a stored
0..1 level falls back to TS3's default of 12 dB.
- Every key press from the global input hook reaches the connected
microphones and the microphone test, so typing attenuation engages
while the user types.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ctrl+F in the tree opens a find bar below it that washes matching rows,
steps the selection through them and can prune the tree down to the
matches and the channels leading to them. ServerTreePanel becomes a
panel holding the scroll pane and the bar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A find bar above the conversations highlights matches and steps through
them, or filters the selected tab down to matching messages. New lines
no longer pull the view away while it is open. Ctrl+F opens it from
anywhere the tree doesn't claim the shortcut.
Note tabs are now found by their scroll pane like other tabs, so sending
checks for a target instead of assuming every tab has one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AWT names the X11 window class after the thread that first touched the
toolkit, so the client shared java-awt-EventDispatchThread with every other
Swing app. Set it to ts3j-client instead (the jar's manifest opens the AWT
internals this needs), and install a matching .desktop launcher and icon in
the user's data directory: without a launcher claiming the class, Plasma falls
back to the process's command line and files the window under Java.
The window icon is now offered at several sizes instead of 16 px only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ts3j: native resend timing, INIT1 resends, one clientinitiv per handshake,
non-blocking disconnect.
- Ignore errors from a connect attempt that has been superseded or cancelled.
- Show the whole cause chain in network error messages.
- -Dts3j.debug=true enables ts3j's protocol logging.
- Tests run with a throwaway user.home instead of the real profile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Connecting to a server with ~560 channels froze the UI for over two
seconds: every model event queued its own full tree rebuild, and each
rebuild expanded row by row with Swing's variable-height layout cache,
which measures every row on each expansion.
Coalesce model-change bursts into one rebuild and switch the tree to the
large-model layout (it already has a fixed row height), which only
measures rows on screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Java2D on X11 (and so under XWayland) truncates its scale to a whole
number, drawing 150 % at 100 %. On Linux, pin Java2D to 1 and hand the
full factor to FlatLaf, taken from XSETTINGS Xft/DPI, KWin's Xwayland
scale or GDK_SCALE, or chosen in Options → Design.
The client's own sizes now go through UIScale: borders, gaps, dialog and
row sizes, icons (rendered at the scaled size, so they stay crisp),
custom-painted components and font sizes in the HTML views.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without forceCreation, maven-jar-plugin kept the previous shaded
ts3-client.jar when the module's classes were unchanged, and the shade then
used it as input: the stale ts3j classes inside it won over the freshly
installed ts3j, so ts3j fixes never reached the built client without a clean.
Also updates ts3j for the empty server-group parsing fix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Avatars are fetched from the server's file repository as /avatar_<id>,
verified against client_flag_avatar and cached on disk; animated GIFs play in
the info panel. Self > Set avatar... uploads or removes our own picture within
the server's size limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The official client keeps what a myTeamSpeak account synchronises as protobuf
Item_Data blobs in the ProtobufItems table of settings.db. Decode them, adopt
them on first run when we have no identity yet, and offer a re-import from
Manage Bookmarks and Identities. The settings.db reader moves from contacts to
a new teamspeak package.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirrors the TeamSpeak 3 client's Contacts: clients can be filed as friend,
blocked or neutral from their context menu or by dragging them into the
Contacts window, given a custom and phonetic nickname, shown under either
name or both, and per contact muted automatically, ignored in server/channel
or private chat and pokes, have their away message hidden and whispers
allowed or denied. Friends are green and blocked clients red in the tree
(optional), new contacts start from per-type defaults ("Set Defaults"), and
the global whisper policy lives in Options -> Contacts. "Change Volume..."
adds a per-client dB gain that a contact entry remembers.
Contacts are stored in ~/.ts3jclient/contacts.txt using the exact record
format the official client keeps in the Contacts table of its settings.db
(field names and codes recovered from the binary), so entries move between
the two verbatim. A small read-only pure-Java SQLite reader imports the
official client's list on first run and on demand, without a driver.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reimplements the official client's ban list: a searchable table of the
server's bans with Add / Remove / Edit, an own-ban filter and highlight,
choosable columns, and the Add/Edit dialog whose IP and name patterns
can be read as IPv4/IPv6 wildcards, fixed strings or regular expressions
(translated to the regexes banadd takes exactly as the official client
does). Editing is add-then-delete, since the protocol has no banedit.
The ban list is read from the notifybanlist burst that answers banlist,
like channelpermlist. The menu item is only shown where the server grants
b_client_ban_list.
Also fixes ServerModel numbering permissions from 0 instead of 1, which
had every selfPermissionValue() lookup reading the neighbouring
permission — including the group-assignment menus.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>