Connecting with a channel moves there once the channel list is in; if
the connection was left again before that, the move ran against the
closed socket and reported an error nobody could act on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
libopus' constants and the encoder settings built on opus_encoder_ctl
(clamping, voice/music signal) move into core, so the Android binding
reuses them rather than copying them from the desktop's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lost packet was concealed by decoding "nothing" into a buffer sized
for the longest Opus frame, and Opus fills whatever it is given: every
lost packet played 120 ms of made-up audio in place of 20 ms, piling up
delay on that speaker's line.
Decoders now conceal through their own call that takes the length to
make up, and the stream asks for the length of the last packet it got.
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>
Android will run core as well, and its runtime stops at Java 17. Pinning
the release makes the compiler reject newer language features and APIs
there, while the desktop modules keep the newest.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sound player was a desktop class only because it decoded wave files
through Java Sound. A small RIFF reader replaces that, so the player
moves to core and every platform gets it from AudioBackend by default.
The reader takes integer PCM up to 32 bits and 32-bit float, in plain
or extensible headers. It decodes all 151 files of the TS3 sound packs
to the same samples Java Sound does.
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>
TS3 pre-processes the microphone with WebRTC's audio processing module.
AudioProcessor ports the stages it enables, in APM's order, per 10 ms at
48 kHz:
- The high-pass filter APM forces on with noise suppression, then the
three-band split and WebRTC's noise suppressor at TS3's four levels.
- The transient suppressor behind "Typing attenuation", which only acts
while it is told about key presses.
- AGC2 adaptive digital, WebRTC's successor to the AGC1 that TS3 runs.
The tests compare against 16-bit outputs of upstream APM builds
(webrtc-audio-processing 2.1, and 1.3 for the transient suppressor).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
channelinfo, clientinfo, channellist and clientlist are query commands
that count heavily against anti-flood; clicking through the tree got
every command refused within seconds. Use what the official client does:
- Channel descriptions via channelgetdescription, whose answer arrives as
an invoker-less channel edit.
- Client version and platform via clientgetvariables, answered as a
client update.
- Connection info takes version and platform from the model and idle
time from notifyconnectioninfo instead of polling clientinfo.
- Drop the channellist/clientlist re-read after connecting: the events
during connect already carry everything, and more (groups, unique ids,
away state).
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>
TextQuery holds a find bar's text and options (match case, whole word,
regex) and matches plain text; HtmlText strips a log entry down to it.
ChatLogStore can now search a whole log rather than only the history
loaded into a chat.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Query clients were already left out of the tree, but their joins, moves
and ping-timeout drops were still logged and could play sounds; TS3 keeps
them out of sight entirely.
- The name field of notifyservergroupclientadded/deleted is the client's
nickname, not the group's, so "added to server group" named the wrong
thing. Resolve the group by id instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Sort a channel among its current siblings with channeledit channel_order,
as the official client does; the server refuses channelmove within the
same parent ("already member of channel").
- Relink siblings when a channel_order edit arrives: the server never
re-announces the neighbours, so the tree drifted out of order after any
reorder.
- Apply password, permanence and max-clients flags from channel list,
create and edit events, and let an emptied topic clear the old one.
- Log moves like TS3: "..., new parent channel is X".
- Play identical sounds that start within 250 ms only once, so bursts
(a channel deleted with its subchannels) don't stack.
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>
The client resolved ts3j 1.0.3 from ~/.m2, so a machine that pulled new
ts3j sources without re-running mvn install silently shipped the old
library - here one without the packet generation fix, which fails
connects to busy servers with "mac check in EAX failed".
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>
Picks up the ts3j fixes for "mac check in EAX failed" disconnects on lossy
links (retransmitted handshake packets now decrypt) and for the handshake
stalling when clientek's ACK is lost, plus tolerating an empty
client_servergroups.
The connect timeout also covers downloading the channel list, which on a
big server over mobile data can take longer than the previous 10 s.
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>
TeamspeakConnection and ChatPanel already use com.ts3client.chatlog, but the
package itself was never committed, so the tree did not build.
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>
The sound mixer rendered as fast as the playback line accepted, running a
full device buffer ahead of real time, so a sound fired while another
played could only join the mix behind all of that queued audio, and each
further one landed later still. Pace the mixer against the clock so it
never commits more than a few frames ahead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Kept as a share of the tree/info column, so it survives new tabs, a resized
window and the next session.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
It overlapped the scroll bar; the viewport is what the scroll bars have
already been taken out of. The layered pane also has to report a size now,
or the split pane above it has nothing to size the panel from.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
As a real label over the text, it stays reachable however long a description
runs, instead of trailing off the end of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A channel's permanent/temporary type is not worth a row, and a client's
platform reads better on the version line: Version: 3.6.2 … on Linux.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A custom tab component swallows the press, so the tabbed pane never selected
the tab itself; the reorder listener already on every tab part now does it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The client's own look-and-feel defaults now live beside LookAndFeelManager as
FlatLaf properties files, which FlatLaf loads on top of each theme's own,
instead of a handful of UIManager.put calls.
The dark theme is built on three surfaces keyed to the toolbar's #282828: the
window chrome, the tab strips and menu bar one step below it at #232323, and
the content the client shows — channel tree, chat log, text fields, lists — at
#1e1e1e. Selections are #364b6f, and channel names in the tree are white; the
chat lines about server events keep the blue they had, which channel names no
longer carry. Everything FlatLaf derives (borders, scroll bars, hover states)
follows from those.
A read-only editor pane is a chat log or an information view here, not a
disabled input, so it keeps the content background rather than FlatLaf's
inactive one; and since neither a text view nor a tree fills its viewport, the
space below the last line takes their background instead of the window's.
The row under the pointer is now washed in the same colour a hovered menu is,
rather than outlined. FlatLaf states that colour as a shift of the surface
beneath it, so it is resolved against the window background exactly as the
menu bar resolves it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The look-and-feel is FlatLaf now, chosen light or dark on the new Design
tab and remembered in the settings. Theme keeps only the colours TeamSpeak
gives meaning to (talking, away, muted, channel and server names) and takes
its surfaces and fonts from the look-and-feel, so a switch recolours the
whole window, chat logs included: the HTML panes share one themed stylesheet
and re-parse their content when the theme changes.
Also fixes the channel tree not scrolling: the tree listened for the mouse
wheel to re-check its hover row, and AWT only forwards a wheel event to the
enclosing scroll pane when the component under the pointer has no wheel
listener of its own. The hover row now follows the viewport instead, which
also covers scrollbar drags and keyboard scrolling.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The counts sat on a line of their own below the choices they belong to,
which also made the Advanced tab the tallest of the four and so decided
how much of the dialog the tab strip took. Each box now keeps its
natural height, and the free space collects below them.
Advanced went from 250px to 220px of preferred height, leaving the tab
strip about a third of the dialog rather than half.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The renderer asks the panel which id is ours rather than reading
ClientEntry.self: our own row usually arrives among the enter-view events
of the connect handshake, before the connection knows its own client id,
so that flag was left false. It is now stamped once the id is known,
which also fixes the "(you)" marker in the info panel.
The inline editor matches, and sits 2px further left so its text lines up
with the label it replaces rather than with its own border.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bolding the name of whoever is speaking re-measures the row while the
label keeps its old width, so a nickname spent every utterance clipped to
an ellipsis. Talking already shows in the client's icon, which is where
TeamSpeak shows it too, so the name keeps its normal weight and colour.
Renaming now starts from the tree's own double-click handling, which
works a row by its whole line, instead of Swing's, which only sees clicks
that land on the label.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>