feat(live): defibrillate live mapping - #1027
Conversation
- Updated `LiveClient` to include `allowCopy` flag, controlling copy permissions. - Implemented checks in `EditorManager` to prevent copying if permissions are not granted. - Added `sendChatMessage` functionality to `LiveClient` and `LiveServer` for chat message broadcasting. - Adjusted UI interactions in `LiveLogTab` to utilize the new chat message sending mechanism. - Updated `LiveDialogs` to set copy permissions during server connection setup. - Incremented `__LIVE_NET_VERSION__` to 6 to reflect these changes.
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enhances the live mapping feature by adding granular control over copy permissions and implementing a functional chat system. These changes improve security and communication during collaborative mapping sessions while ensuring network compatibility through a version update. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughThis PR extends live collaborative mapping with copy-paste permission control and refactors chat messaging through a polymorphic interface. The server can now restrict copy operations per session, which the client receives during handshake and the editor enforces. Chat messaging is unified via a virtual method in the socket base class, and the live menu is activated in the UI. ChangesLive Collaboration Enhancements: Copy Permissions and Chat Messaging
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Infer (1.2.0)source/live/live_client.cppsource/live/live_client.cpp:18:10: fatal error: 'app/main.h' file not found ... [truncated 1062 characters] ... install/lib/clang/18/include" source/live/live_peer.cppsource/live/live_peer.cpp:18:10: fatal error: 'app/main.h' file not found ... [truncated 1056 characters] ... g/install/lib/clang/18/include" source/editor/managers/editor_manager.cppsource/editor/managers/editor_manager.cpp:5:10: fatal error: 'app/main.h' file not found ... [truncated 1096 characters] ... 18/include"
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request enables the 'Live' mapping menu, increments the network version, and implements a feature to restrict copying/cutting during live sessions. It also updates the chat system to send messages on Enter and fixes a potential crash in client list updates. Feedback highlights several improvement opportunities to align with the C++20/23 style guide, such as using default member initializers in headers, using const auto* for observation, and using auto with proper string trimming. Additionally, a thread-safety issue was identified regarding the static local id variable used for generating peer IDs in LiveServer::acceptClient().
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| } else { | ||
| auto peer = std::make_unique<LivePeer>(this, std::move(*socket)); | ||
| peer->log = log; | ||
| peer->id = id; |
There was a problem hiding this comment.
Using a static local variable id inside acceptClient() to generate peer IDs is a design issue. It is not thread-safe if acceptClient is called from multiple threads, and it shares the ID counter across all LiveServer instances (violating modularity). Consider refactoring id to be a member variable of LiveServer (e.g., uint32_t nextPeerId = 0;).
| wxString text = input->GetValue().Trim(); | ||
| if (text.empty()) { | ||
| return; | ||
| } | ||
| socket->sendChatMessage(text); | ||
| input->Clear(); |
There was a problem hiding this comment.
Use auto for the local variable type since the type of input->GetValue() is obvious. Additionally, Trim() only trims trailing whitespace by default. To trim both leading and trailing whitespace, chain Trim(true).Trim(false). This also avoids calling Trim() on a temporary object directly.
auto text = input->GetValue();
text.Trim(true).Trim(false);
if (text.empty()) {
return;
}
socket->sendChatMessage(text);
input->Clear();References
- Apply incremental improvements: auto where type is obvious. (link)
| LiveClient* liveClient = editor->live_manager.GetClient(); | ||
| if (liveClient && !liveClient->isAllowCopy()) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
Use const auto* to declare liveClient since it is only used for observation and is not mutated. This aligns with the style guide's recommendation to use auto where the type is obvious and to enforce const-correctness.
const auto* liveClient = editor->live_manager.GetClient();
if (liveClient && !liveClient->isAllowCopy()) {
return;
}References
- Apply incremental improvements: auto where type is obvious. (link)
| LiveClient* liveClient = editor->live_manager.GetClient(); | ||
| if (liveClient && !liveClient->isAllowCopy()) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
Use const auto* to declare liveClient for const-correctness and to adhere to the style guide's recommendation of using auto where appropriate.
const auto* liveClient = editor->live_manager.GetClient();
if (liveClient && !liveClient->isAllowCopy()) {
return;
}References
- Apply incremental improvements: auto where type is obvious. (link)
|
|
||
| std::unique_ptr<Editor> editor; | ||
|
|
||
| bool allowCopy; |
There was a problem hiding this comment.
In modern C++20/23, prefer initializing member variables directly in the class definition (header) rather than in the constructor initializer list. This prevents uninitialized member bugs and keeps the constructor clean.
| bool allowCopy; | |
| bool allowCopy = false; |
References
- Every new and modified file MUST use C++20/23 features. Prefer default member initializers in class definitions. (link)
| LiveSocket(), | ||
| readMessage(), queryNodeList(), currentOperation(), | ||
| resolver(nullptr), socket(nullptr), editor(nullptr), stopped(false) { | ||
| resolver(nullptr), socket(nullptr), editor(nullptr), allowCopy(false), stopped(false) { |
|
|
||
| uint32_t clientIds; | ||
| uint16_t port; | ||
| bool allowCopy; |
There was a problem hiding this comment.
In modern C++20/23, prefer initializing member variables directly in the class definition (header) rather than in the constructor initializer list.
| bool allowCopy; | |
| bool allowCopy = false; |
References
- Every new and modified file MUST use C++20/23 features. Prefer default member initializers in class definitions. (link)
| LiveSocket(), | ||
| clients(), acceptor(nullptr), socket(nullptr), editor(&editor), | ||
| clientIds(0), port(0), stopped(false) { | ||
| clientIds(0), port(0), allowCopy(false), stopped(false) { |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
source/live/live_server.cpp (1)
88-110:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftRace condition on static
idcounter.The static
idvariable at line 89 is incremented at line 110 (clients.emplace(id++, ...)) without synchronization. SinceacceptClient()schedules async callbacks that can execute concurrently when multiple clients connect simultaneously, two accept operations could read and increment the sameidvalue, assigning duplicate IDs to different peers.Consider using
std::atomic<uint32_t>or protecting the increment withclientMutex.🔒 Proposed fix using atomic
void LiveServer::acceptClient() { - static uint32_t id = 0; + static std::atomic<uint32_t> id{0}; if (stopped) { return; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@source/live/live_server.cpp` around lines 88 - 110, The static uint32_t id in LiveServer::acceptClient is incremented without synchronization causing a race when multiple async accepts complete; fix by making the counter atomic or protecting increments with clientMutex: replace the static id with std::atomic<uint32_t> id (or a member std::atomic<uint32_t> idCounter) and use id.fetch_add(1) (or increment under the clientMutex before calling clients.emplace), ensuring LiveServer::acceptClient and the clients.emplace(id..., ...) use the synchronized/atomic value to avoid duplicate IDs.source/editor/managers/editor_manager.cpp (2)
368-386:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winNo user feedback when copy is denied.
When a client attempts to cut without
allowCopypermission (lines 379-381), the operation silently returns without any notification to the user. Consider adding a status message or visual feedback to inform users why the operation didn't execute.The enforcement logic itself is correct.
💬 Proposed feedback addition
LiveClient* liveClient = editor->live_manager.GetClient(); if (liveClient && !liveClient->isAllowCopy()) { + g_status.SetStatusText("Copy operations are not permitted on this live session."); return; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@source/editor/managers/editor_manager.cpp` around lines 368 - 386, The DoCut path silently returns when LiveClient::isAllowCopy() denies the operation; add user feedback before returning so users know why the cut failed. In EditorManager::DoCut, when liveClient exists and !liveClient->isAllowCopy(), call the GUI status/notification API (e.g. g_gui.ShowStatus or g_gui.SetStatusMessage) with a short message like "Cut denied: copy permission required" (or use your app's standard localization key), then return; keep the existing early-return logic and do not perform editor->copybuffer.cut or view refresh in this branch.
388-406:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winNo user feedback when copy is denied.
Same concern as
DoCut: when copy is blocked (lines 399-401), users receive no feedback explaining why their action had no effect.💬 Proposed feedback addition
LiveClient* liveClient = editor->live_manager.GetClient(); if (liveClient && !liveClient->isAllowCopy()) { + g_status.SetStatusText("Copy operations are not permitted on this live session."); return; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@source/editor/managers/editor_manager.cpp` around lines 388 - 406, The DoCopy method silently returns when a LiveClient forbids copying (LiveClient::isAllowCopy()), so add user feedback before the early return: when liveClient exists and isAllowCopy() is false, call the GUI feedback mechanism (e.g., display a status/message or dialog via g_gui — matching how DoCut reports denials) to inform the user the copy was blocked, then return; keep g_gui.RefreshView() and g_gui.root->UpdateMenubar() behavior unchanged for successful copies.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@source/live/live_server.cpp`:
- Around line 148-153: LiveServer::broadcastChat currently dereferences log
without null-check which can race with LiveSocket (log starts as nullptr) and
createLogWindow (which sets log) since bind/async networking (triggered in
LiveDialogs::ShowHostDialog) may call LivePeer::parseChatMessage ->
LiveServer::broadcastChat before log is set; fix by adding a guard around all
uses of log inside LiveServer::broadcastChat (e.g., if (log) { log->Chat(...); }
) or otherwise ensure createLogWindow runs before bind starts accepting packets;
update references in broadcastChat so every call to log->... is protected.
In `@source/live/live_tab.cpp`:
- Around line 183-193: LiveLogTab::OnChat uses wxString::Trim() which only trims
the right side by default, so leading whitespace can be sent; update the code
around the input->GetValue().Trim() call to trim both leading and trailing
whitespace (e.g., call text.Trim(false); text.Trim(true); or use an equivalent
two-sided/Strip method) before checking text.empty(), referencing
LiveLogTab::OnChat and the local variable text.
---
Outside diff comments:
In `@source/editor/managers/editor_manager.cpp`:
- Around line 368-386: The DoCut path silently returns when
LiveClient::isAllowCopy() denies the operation; add user feedback before
returning so users know why the cut failed. In EditorManager::DoCut, when
liveClient exists and !liveClient->isAllowCopy(), call the GUI
status/notification API (e.g. g_gui.ShowStatus or g_gui.SetStatusMessage) with a
short message like "Cut denied: copy permission required" (or use your app's
standard localization key), then return; keep the existing early-return logic
and do not perform editor->copybuffer.cut or view refresh in this branch.
- Around line 388-406: The DoCopy method silently returns when a LiveClient
forbids copying (LiveClient::isAllowCopy()), so add user feedback before the
early return: when liveClient exists and isAllowCopy() is false, call the GUI
feedback mechanism (e.g., display a status/message or dialog via g_gui —
matching how DoCut reports denials) to inform the user the copy was blocked,
then return; keep g_gui.RefreshView() and g_gui.root->UpdateMenubar() behavior
unchanged for successful copies.
In `@source/live/live_server.cpp`:
- Around line 88-110: The static uint32_t id in LiveServer::acceptClient is
incremented without synchronization causing a race when multiple async accepts
complete; fix by making the counter atomic or protecting increments with
clientMutex: replace the static id with std::atomic<uint32_t> id (or a member
std::atomic<uint32_t> idCounter) and use id.fetch_add(1) (or increment under the
clientMutex before calling clients.emplace), ensuring LiveServer::acceptClient
and the clients.emplace(id..., ...) use the synchronized/atomic value to avoid
duplicate IDs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b2319d09-213e-40a5-91f6-d513da15950c
📒 Files selected for processing (11)
data/menubar.xmlsource/app/definitions.hsource/editor/managers/editor_manager.cppsource/live/live_client.cppsource/live/live_client.hsource/live/live_peer.cppsource/live/live_server.cppsource/live/live_server.hsource/live/live_socket.hsource/live/live_tab.cppsource/ui/live_dialogs.cpp
💤 Files with no reviewable changes (1)
- data/menubar.xml
| void LiveServer::updateClientList() const { | ||
| std::lock_guard<std::mutex> lock(clientMutex); | ||
| log->UpdateClientList(clients); | ||
| if (log) { | ||
| log->UpdateClientList(clients); | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find all log pointer dereferences to verify null-safety
rg -n 'log->' --type=cpp -g 'live_*.cpp' -C2Repository: karolak6612/remeres-map-editor-redux
Length of output: 7950
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Inspect live_server.cpp around the broadcastChat() implementation and any log lifecycle points.
echo "=== live_server.cpp: broadcastChat + nearby ==="
rg -n "broadcastChat\\(" source/live/live_server.cpp
echo
rg -n "void LiveServer::updateClientList\\(" source/live/live_server.cpp
echo
rg -n "\\blog\\s*=\\s*nullptr|\\blog\\s*=\\s*newd\\s+LiveLogTab|log = nullptr|log\\s*=" source/live/live_server.cpp
echo
# Print a small surrounding window around those areas.
python3 - <<'PY'
import re, pathlib
p = pathlib.Path("source/live/live_server.cpp")
txt = p.read_text(errors="ignore").splitlines()
need = set()
for i,line in enumerate(txt, start=1):
if "broadcastChat(" in line or "updateClientList(" in line or "log = nullptr" in line or "newd LiveLogTab" in line or "log->Chat" in line:
need.add(i)
# include a bit of context around found lines
ctx = set()
for i in sorted(need):
for j in range(max(1,i-8), min(len(txt), i+8)+1):
ctx.add(j)
for j in sorted(ctx):
print(f"{j:4d}: {txt[j-1]}")
PY
# 2) Find all call sites of broadcastChat() across the codebase.
echo
echo "=== Call sites of broadcastChat() ==="
rg -n "broadcastChat\\(" -S --type=cpp source/liveRepository: karolak6612/remeres-map-editor-redux
Length of output: 3590
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== live_server.h: find log member ==="
rg -n "class LiveServer|\\blog\\b" source/live/live_server.h -n
echo
rg -n "LiveServer::(LiveServer\\(|~LiveServer\\(|shutdown\\(|stop\\(|close\\(|Destroy\\(|createLogWindow\\(|sendChatMessage\\(|broadcastChat\\())" -S source/live/live_server.cpp
echo
echo "=== live_server.cpp: around broadcastChat caller(s) and log lifecycle ==="
python3 - <<'PY'
import pathlib
p = pathlib.Path("source/live/live_server.cpp")
lines = p.read_text(errors="ignore").splitlines()
targets = [
("shutdown-ish", "log = nullptr"),
("peer log assignment", "peer->log = log"),
("createLogWindow", "createLogWindow"),
("sendChatMessage", "sendChatMessage"),
("broadcastChat", "broadcastChat"),
]
def find_lines(needle):
idx=[]
for i,l in enumerate(lines, start=1):
if needle in l:
idx.append(i)
return idx
for name, needle in targets:
idx=find_lines(needle)
if not idx:
print(f"-- {name}: no matches for {needle}")
continue
for i in idx[:3]:
print(f"\n-- {name} (line {i})")
for j in range(max(1,i-10), min(len(lines), i+10)+1):
print(f"{j:4d}: {lines[j-1]}")
PY
echo
echo "=== live_peer.cpp: around server->broadcastChat(...) ==="
rg -n "server->broadcastChat" -C6 source/live/live_peer.cpp
echo
echo "=== search for createLogWindow() call sites ==="
rg -n "createLogWindow\\(" source/live -S
echoRepository: karolak6612/remeres-map-editor-redux
Length of output: 489
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== live_server.h: locate log declaration ==="
rg -n "\\blog\\b" source/live/live_server.h
echo
echo "=== live_server.cpp: locate log lifecycle assignments ==="
rg -n "log\\s*=\\s*nullptr|log\\s*=\\s*newd\\s+LiveLogTab|\\blog->" source/live/live_server.cpp -C2
echo
echo "=== live_server.cpp: show constructor/destructor and shutdown-ish regions ==="
rg -n "LiveServer::(LiveServer|~LiveServer|shutdown|stop|close|Destroy|createLogWindow)" source/live/live_server.cpp
echo
# Print around createLogWindow
echo
echo "=== live_server.cpp: full createLogWindow() context ==="
rg -n "LiveLogTab\\* LiveServer::createLogWindow" source/live/live_server.cpp
python3 - <<'PY'
import pathlib, re
p=pathlib.Path("source/live/live_server.cpp")
lines=p.read_text(errors="ignore").splitlines()
# find line with signature
sig=None
for i,l in enumerate(lines, start=1):
if "LiveLogTab* LiveServer::createLogWindow" in l:
sig=i;break
assert sig
start=max(1,sig-5)
end=min(len(lines), sig+120)
for j in range(start, end+1):
print(f"{j:4d}: {lines[j-1]}")
PY
echo
echo "=== live_server.cpp: broadcastChat() context ==="
python3 - <<'PY'
import pathlib
p=pathlib.Path("source/live/live_server.cpp")
lines=p.read_text(errors="ignore").splitlines()
sig=None
for i,l in enumerate(lines, start=1):
if "void LiveServer::broadcastChat" in l:
sig=i;break
assert sig
start=max(1,sig-20)
end=min(len(lines), sig+60)
for j in range(start, end+1):
print(f"{j:4d}: {lines[j-1]}")
PY
echo
echo "=== call sites: createLogWindow() ==="
rg -n "createLogWindow\\(" source/live -SRepository: karolak6612/remeres-map-editor-redux
Length of output: 126
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== live_server.h: show around class LiveServer definition ==="
python3 - <<'PY'
import pathlib
p=pathlib.Path("source/live/live_server.h")
txt=p.read_text(errors="ignore").splitlines()
for i,l in enumerate(txt, start=1):
if "class LiveServer" in l:
start=max(1,i-20); end=min(len(txt), i+120)
for j in range(start,end+1):
print(f"{j:4d}: {txt[j-1]}")
break
PY
echo
echo "=== live_server.h: find LiveLogTab/log-related identifiers ==="
rg -n "LiveLogTab|\\blog\\b|\\blog\\s*[;=]|\\blog\\s*\\w+|UpdateClientList|Chat\\(" source/live/live_server.h || true
echo
echo "=== live_server.cpp: find log member/type declarations/uses ==="
rg -n "LiveLogTab\\*|LiveLogTab\\s+\\w+|\\blog\\b\\s*[;=]|\\blog\\s*\\.|\\blog->" source/live/live_server.cpp -S || true
echo
echo "=== live_server.cpp: show all assignments to log ==="
rg -n "log\\s*=" source/live/live_server.cpp -SRepository: karolak6612/remeres-map-editor-redux
Length of output: 3470
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== live_socket.h: find log declaration (likely in base class) ==="
rg -n "\\blog\\b|LiveLogTab" source/live/live_socket.h source/live/live_socket.cpp || true
echo
echo "=== live_server.cpp: find LiveServer constructor/destructor/close() and any log-related behavior ==="
rg -n "LiveServer::LiveServer\\(|LiveServer::~LiveServer\\(|void LiveServer::close\\(|void LiveServer::acceptClient\\(|void LiveServer::removeClient\\(" source/live/live_server.cpp
python3 - <<'PY'
import pathlib, re
p=pathlib.Path("source/live/live_server.cpp")
lines=p.read_text(errors="ignore").splitlines()
def print_region(start_pat, window=120):
for i,l in enumerate(lines, start=1):
if re.search(start_pat, l):
s=max(1,i-10); e=min(len(lines), i+window)
print(f"\n--- region around line {i} matching {start_pat} ---")
for j in range(s,e+1):
print(f"{j:4d}: {lines[j-1]}")
break
print_region(r"LiveServer::LiveServer\\(")
print_region(r"~LiveServer\\(")
print_region(r"void LiveServer::close\\(")
PY
echo
echo "=== call sites: createLogWindow() ==="
rg -n "createLogWindow\\(" source/live -S
echo
echo "=== call sites: sendChatMessage() ==="
rg -n "sendChatMessage\\(" source/live -S
echo
echo "=== call sites: broadcastChat() ==="
rg -n "broadcastChat\\(" source/live -S
echo
echo "=== live_peer.cpp: inspect how chat packets reach server->broadcastChat ==="
rg -n "PACKET_SERVER_TALK|Chat\\(|broadcastChat\\(" source/live/live_peer.cpp -C2Repository: karolak6612/remeres-map-editor-redux
Length of output: 2118
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== live_socket.h around log ==="
python3 - <<'PY'
import pathlib
p=pathlib.Path("source/live/live_socket.h")
lines=p.read_text(errors="ignore").splitlines()
for i,l in enumerate(lines, start=1):
if "LiveLogTab* log" in l or "LiveLogTab* log;" in l or "LiveLogTab* log" in l:
start=max(1,i-15); end=min(len(lines), i+15)
for j in range(start,end+1):
print(f"{j:4d}: {lines[j-1]}")
break
PY
echo
echo "=== live_socket.cpp around log init and any null checks ==="
python3 - <<'PY'
import pathlib
p=pathlib.Path("source/live/live_socket.cpp")
lines=p.read_text(errors="ignore").splitlines()
# show around constructor init list line containing "log(nullptr)"
for i,l in enumerate(lines, start=1):
if "log(nullptr)" in l:
start=max(1,i-20); end=min(len(lines), i+20)
for j in range(start,end+1):
print(f"{j:4d}: {lines[j-1]}")
break
PY
echo
echo "=== live_server.cpp: show constructor/destructor/close/accept/remove regions (small) ==="
python3 - <<'PY'
import pathlib, re
p=pathlib.Path("source/live/live_server.cpp")
lines=p.read_text(errors="ignore").splitlines()
def show_sig(sig):
for i,l in enumerate(lines, start=1):
if sig in l:
start=max(1,i-10); end=min(len(lines), i+120)
print(f"\n--- {sig} near line {i} ---")
for j in range(start,end+1):
print(f"{j:4d}: {lines[j-1]}")
return
print(f"missing {sig}")
show_sig("LiveServer::LiveServer")
show_sig("LiveServer::~LiveServer")
show_sig("void LiveServer::close")
show_sig("void LiveServer::acceptClient")
show_sig("void LiveServer::removeClient")
PY
echo
echo "=== createLogWindow() call sites ==="
rg -n "createLogWindow\\(" source -S
echo
echo "=== live_server.cpp: broadcastChat() implementation + surrounding ==="
python3 - <<'PY'
import pathlib
p=pathlib.Path("source/live/live_server.cpp")
lines=p.read_text(errors="ignore").splitlines()
for i,l in enumerate(lines, start=1):
if "void LiveServer::broadcastChat" in l:
start=max(1,i-15); end=min(len(lines), i+60)
for j in range(start,end+1):
print(f"{j:4d}: {lines[j-1]}")
break
PY
echo
echo "=== live_server.cpp: sendChatMessage() context ==="
python3 - <<'PY'
import pathlib
p=pathlib.Path("source/live/live_server.cpp")
lines=p.read_text(errors="ignore").splitlines()
for i,l in enumerate(lines, start=1):
if "void LiveServer::sendChatMessage" in l:
start=max(1,i-10); end=min(len(lines), i+40)
for j in range(start,end+1):
print(f"{j:4d}: {lines[j-1]}")
break
PY
echo
echo "=== live_peer.cpp: locate PACKET_SERVER_TALK -> broadcastChat chain ==="
rg -n "PACKET_SERVER_TALK|broadcastChat\\(" source/live/live_peer.cpp -C4Repository: karolak6612/remeres-map-editor-redux
Length of output: 25410
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== live_dialogs.cpp: context around LiveServer::createLogWindow call ==="
rg -n "createLogWindow\\(" source/ui/live_dialogs.cpp -n
python3 - <<'PY'
import pathlib
p=pathlib.Path("source/ui/live_dialogs.cpp")
lines=p.read_text(errors="ignore").splitlines()
targets=[]
for i,l in enumerate(lines, start=1):
if "createLogWindow" in l:
targets.append(i)
for i in targets:
print(f"\n--- around createLogWindow at line {i} ---")
for j in range(max(1,i-30), min(len(lines), i+30)+1):
print(f"{j:4d}: {lines[j-1]}")
PY
echo
echo "=== live_dialogs.cpp: search for LiveServer bind/start ordering ==="
rg -n "bind\\(|acceptClient\\(|sendChatMessage\\(|PACKET_SERVER_TALK|broadcastChat|createLogWindow\\(" source/ui/live_dialogs.cpp -S
echo
echo "=== live_server.cpp: any guard against stopped/acceptor during chat handling? ==="
rg -n "parseChatMessage|stopped|receiveHeader|PACKET_SERVER_TALK" source/live -SRepository: karolak6612/remeres-map-editor-redux
Length of output: 5970
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== live_packets.h: find TALK packet IDs ==="
rg -n "TALK" source/live/live_packets.h -n
echo
echo "=== live_peer.cpp: find parseChatMessage call site and switch cases ==="
rg -n "parseChatMessage\\(|PACKET_.*TALK|PACKET_.*CHAT" source/live/live_peer.cpp -n -S -C3
echo
echo "=== live_peer.cpp: show receiveHeader() and message handling block (around packet switch) ==="
python3 - <<'PY'
import pathlib
p=pathlib.Path("source/live/live_peer.cpp")
lines=p.read_text(errors="ignore").splitlines()
# find the line containing "switch" near receiveHeader/message loop
for i,l in enumerate(lines, start=1):
if "receiveHeader" in l and "void LivePeer::receiveHeader" in l:
start=i-5
break
else:
start=1
# print around first 220 lines maybe
for j in range(start, min(len(lines), start+220)):
print(f"{j:4d}: {lines[j-1]}")
PY
echo
echo "=== live_peer.cpp: show around PACKET_SERVER_TALK handling (if any) and chat send ==="
rg -n "PACKET_SERVER_TALK|sendChat|Chat\\(" source/live/live_peer.cpp -n -S -C4Repository: karolak6612/remeres-map-editor-redux
Length of output: 8754
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== locate NetworkConnection implementation ==="
rg -n "class NetworkConnection|NetworkConnection::" source/net -S
echo
echo "=== inspect NetworkConnection::start() and threading/run-loop ==="
rg -n "NetworkConnection::start\\(|io_context\\.run\\(|run\\(\\)|get_service\\(|std::thread" source/net -S
# show relevant sections
python3 - <<'PY'
import pathlib, re
paths = [p for p in pathlib.Path("source").rglob("net_connection.*") if p.is_file()]
print("Found:", [str(x) for x in paths])
PYRepository: karolak6612/remeres-map-editor-redux
Length of output: 1504
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import pathlib
p=pathlib.Path("source/net/net_connection.cpp")
lines=p.read_text(errors="ignore").splitlines()
# print around start()
for i,l in enumerate(lines, start=1):
if "bool NetworkConnection::start()" in l:
start=i;break
else:
start=1
s=max(1,start-20); e=min(len(lines), start+120)
print("=== net_connection.cpp around start() ===")
for j in range(s,e+1):
print(f"{j:4d}: {lines[j-1]}")
PY
echo
echo "=== net_connection.cpp around stop() ==="
python3 - <<'PY'
import pathlib, re
p=pathlib.Path("source/net/net_connection.cpp")
lines=p.read_text(errors="ignore").splitlines()
for i,l in enumerate(lines, start=1):
if "void NetworkConnection::stop()" in l:
start=i;break
else:
start=1
s=max(1,start-20); e=min(len(lines), start+120)
for j in range(s,e+1):
print(f"{j:4d}: {lines[j-1]}")
PY
echo
echo "=== net_connection.cpp around get_service() ==="
python3 - <<'PY'
import pathlib
p=pathlib.Path("source/net/net_connection.cpp")
lines=p.read_text(errors="ignore").splitlines()
for i,l in enumerate(lines, start=1):
if "boost::asio::io_context& NetworkConnection::get_service()" in l:
start=i;break
else:
start=1
s=max(1,start-10); e=min(len(lines), start+40)
for j in range(s,e+1):
print(f"{j:4d}: {lines[j-1]}")
PYRepository: karolak6612/remeres-map-editor-redux
Length of output: 3767
Unconditional log deref in LiveServer::broadcastChat() can crash when log == nullptr
LiveSocketinitializeslogtonullptr, andLiveServer::createLogWindow()(which assignslog) is called afterLiveServer::bind()starts async networking inLiveDialogs::ShowHostDialog.NetworkConnectionruns the io_context on a background thread, soLivePeer::parseChatMessage()->LiveServer::broadcastChat()can execute beforelogis set;broadcastChat()unconditionally doeslog->Chat(...)(~source/live/live_server.cpp:248-264).- Add a
if (log) { ... }guard inbroadcastChat()or restructure startup sologis created before accepting/processing packets.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@source/live/live_server.cpp` around lines 148 - 153,
LiveServer::broadcastChat currently dereferences log without null-check which
can race with LiveSocket (log starts as nullptr) and createLogWindow (which sets
log) since bind/async networking (triggered in LiveDialogs::ShowHostDialog) may
call LivePeer::parseChatMessage -> LiveServer::broadcastChat before log is set;
fix by adding a guard around all uses of log inside LiveServer::broadcastChat
(e.g., if (log) { log->Chat(...); } ) or otherwise ensure createLogWindow runs
before bind starts accepting packets; update references in broadcastChat so
every call to log->... is protected.
| void LiveLogTab::OnChat(wxCommandEvent& evt) { | ||
| if (!socket) { | ||
| return; | ||
| } | ||
| wxString text = input->GetValue().Trim(); | ||
| if (text.empty()) { | ||
| return; | ||
| } | ||
| socket->sendChatMessage(text); | ||
| input->Clear(); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
wxWidgets wxString Trim method behavior default parameter
💡 Result:
In wxWidgets, the wxString::Trim method accepts a single boolean parameter, fromRight, which determines whether whitespace is removed from the end or the beginning of the string [1][2]. The default value for this parameter is true [1][3]. When called without arguments, wxString::Trim defaults to removing whitespace from the right (end) of the string [1][4]. If you pass false to the method, it will remove whitespace from the left (beginning) of the string instead [1][2]. The method removes characters identified as whitespace, including spaces, tabs, form feeds, newlines, and carriage returns [1][3]. The method returns a reference to the modified string [1][5].
Citations:
- 1: https://docs.wxwidgets.org/stable/classwx_string.html
- 2: https://docs.wxwidgets.org/latest/classwx_string.html
- 3: https://docs.wxwidgets.org/2.8/wx_wxstring.html
- 4: https://docs.wxwidgets.org/3.3/classwx_string.html
- 5: https://docs.wxwidgets.org/3.3.1/classwx_string.html
Fix whitespace trimming in LiveLogTab::OnChat
wxString::Trim() called with no args defaults to trimming from the right only, so leading whitespace is preserved and can be sent (e.g., " hello"). Whitespace-only input will still be trimmed to empty and blocked by the existing text.empty() check.
Trim both leading and trailing whitespace
- wxString text = input->GetValue().Trim();
+ wxString text = input->GetValue().Trim(true).Trim(false);
if (text.empty()) {
return;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| void LiveLogTab::OnChat(wxCommandEvent& evt) { | |
| if (!socket) { | |
| return; | |
| } | |
| wxString text = input->GetValue().Trim(); | |
| if (text.empty()) { | |
| return; | |
| } | |
| socket->sendChatMessage(text); | |
| input->Clear(); | |
| } | |
| void LiveLogTab::OnChat(wxCommandEvent& evt) { | |
| if (!socket) { | |
| return; | |
| } | |
| wxString text = input->GetValue().Trim(true).Trim(false); | |
| if (text.empty()) { | |
| return; | |
| } | |
| socket->sendChatMessage(text); | |
| input->Clear(); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@source/live/live_tab.cpp` around lines 183 - 193, LiveLogTab::OnChat uses
wxString::Trim() which only trims the right side by default, so leading
whitespace can be sent; update the code around the input->GetValue().Trim() call
to trim both leading and trailing whitespace (e.g., call text.Trim(false);
text.Trim(true); or use an equivalent two-sided/Strip method) before checking
text.empty(), referencing LiveLogTab::OnChat and the local variable text.
LiveClientto includeallowCopyflag, controlling copy permissions.EditorManagerto prevent copying if permissions are not granted.sendChatMessagefunctionality toLiveClientandLiveServerfor chat message broadcasting.LiveLogTabto utilize the new chat message sending mechanism.LiveDialogsto set copy permissions during server connection setup.__LIVE_NET_VERSION__to 6 to reflect these changes.